source: lib/protocols_l3.c @ c2afda6

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since c2afda6 was c2afda6, checked in by Shane Alcock <salcock@…>, 12 years ago
  • Adjusted the "shortcut" protocol access functions to return NULL if a full header (minus options) is not present.
  • Property mode set to 100644
File size: 10.9 KB
Line 
1/* Protocol decodes for Layer 3 protocols */
2#include "libtrace.h"
3#include "protocols.h"
4#include <assert.h>
5#include <stdlib.h>
6#include "config.h"
7
8#ifdef HAVE_NETPACKET_PACKET_H
9#include <sys/socket.h>
10#include <netpacket/packet.h>
11#include <net/ethernet.h>
12#include <net/if_arp.h>
13#include <string.h>
14#endif
15
16libtrace_ip_t *trace_get_ip(libtrace_packet_t *packet) 
17{
18        uint16_t ethertype;
19        void *ret;
20
21        uint32_t remaining = trace_get_capture_length(packet);
22
23        ret = trace_get_layer3(packet,&ethertype,&remaining);
24
25        if (!ret || ethertype!=TRACE_ETHERTYPE_IP)
26                return NULL;
27
28        /* Make sure we have at least a base IPv4 header */
29        if (remaining < sizeof(libtrace_ip_t)) 
30                return NULL;
31       
32        /* Not an IPv4 packet */
33        if (((libtrace_ip_t*)ret)->ip_v != 4)
34                return NULL;
35
36        return (libtrace_ip_t*)ret;
37}
38
39libtrace_ip6_t *trace_get_ip6(libtrace_packet_t *packet) 
40{
41        uint16_t ethertype;
42        void *ret;
43
44        uint32_t remaining = trace_get_capture_length(packet);
45
46        ret = trace_get_layer3(packet,&ethertype,&remaining);
47
48        if (!ret || ethertype!=TRACE_ETHERTYPE_IPV6)
49                return NULL;
50
51        /* Make sure we have at least the base IPv6 header */
52        if (remaining < sizeof(libtrace_ip6_t))
53                return NULL;
54
55        return (libtrace_ip6_t*)ret;
56}
57
58#define SW_IP_OFFMASK 0x1fff
59
60DLLEXPORT void *trace_get_payload_from_ip(libtrace_ip_t *ipptr, uint8_t *prot,
61                uint32_t *remaining) 
62{
63        void *trans_ptr = 0;
64
65        assert(ipptr != NULL);
66       
67        /* Er? IPv5? */
68        if (ipptr->ip_v != 4)
69                return NULL;
70
71        if ((ntohs(ipptr->ip_off) & SW_IP_OFFMASK) != 0) {
72                if (remaining)
73                        *remaining = 0;         
74                return NULL;
75        }
76
77        if (remaining) {
78                if (*remaining<(ipptr->ip_hl*4U)) {
79                        *remaining = 0;
80                        return NULL;
81                }
82                *remaining-=(ipptr->ip_hl * 4);
83        }
84
85        trans_ptr = (void *)((char *)ipptr + (ipptr->ip_hl * 4));
86
87        if (prot) *prot = ipptr->ip_p;
88
89        return trans_ptr;
90}
91
92void *trace_get_payload_from_ip6(libtrace_ip6_t *ipptr, uint8_t *prot,
93                uint32_t *remaining) 
94{
95        void *payload = (char*)ipptr+sizeof(libtrace_ip6_t);
96        uint8_t nxt;
97
98        assert (ipptr != NULL);
99        nxt = ipptr->nxt;       
100        if (remaining) {
101                if (*remaining<sizeof(libtrace_ip6_t)) {
102                        *remaining = 0;
103                        return NULL;
104                }
105                *remaining-=sizeof(libtrace_ip6_t);
106        }
107
108        while(1) {
109                switch (nxt) {
110                        case 0: /* hop by hop options */
111                        case TRACE_IPPROTO_ROUTING:
112                        case TRACE_IPPROTO_FRAGMENT:
113                        case TRACE_IPPROTO_ESP:
114                        case TRACE_IPPROTO_AH:
115                        case TRACE_IPPROTO_DSTOPTS:
116                                {
117                                        uint16_t len=((libtrace_ip6_ext_t*)payload)->len
118                                        +sizeof(libtrace_ip6_ext_t);
119
120                                        if (remaining) {
121                                                if (*remaining < len) {
122                                                        /* Snap too short */
123                                                        *remaining = 0;
124                                                        return NULL;
125                                                }
126                                                *remaining-=len;
127                                        }
128
129                                        payload=(char*)payload+len;
130                                        nxt=((libtrace_ip6_ext_t*)payload)->nxt;
131                                        continue;
132                                }
133                        default:
134                                if (prot) *prot=nxt;
135                                return payload;
136                }
137        }
138}
139
140DLLEXPORT void *trace_get_layer3(const libtrace_packet_t *packet,
141                uint16_t *ethertype,
142                uint32_t *remaining)
143{
144        void *iphdr;
145        uint16_t dummy_ethertype;
146        void *link;
147        uint32_t dummy_remaining;
148        libtrace_linktype_t linktype;
149
150        if (!ethertype) ethertype=&dummy_ethertype;
151
152        if (!remaining) remaining=&dummy_remaining;
153
154        /* use l3 cache */
155        if (packet->l3_header)
156        {
157                link = trace_get_packet_buffer(packet,&linktype,remaining);
158
159                if (!link)
160                        return NULL;
161
162                *ethertype = packet->l3_ethertype;
163                *remaining -= (packet->l3_header - link);
164
165                return packet->l3_header;
166        }
167
168        link = trace_get_layer2(packet,&linktype,remaining);
169        iphdr = trace_get_payload_from_layer2(
170                        link,
171                        linktype,
172                        ethertype,
173                        remaining);
174
175        for(;;) {
176                if (!iphdr || *remaining == 0)
177                        break;
178                switch(*ethertype) {
179                case TRACE_ETHERTYPE_8021Q: /* VLAN */
180                        iphdr=trace_get_payload_from_vlan(
181                                          iphdr,ethertype,remaining);
182                        continue;
183                case TRACE_ETHERTYPE_MPLS: /* MPLS */
184                        iphdr=trace_get_payload_from_mpls(
185                                          iphdr,ethertype,remaining);
186
187                        if (iphdr && ethertype == 0x0) {
188                                iphdr=trace_get_payload_from_ethernet(
189                                                iphdr,ethertype,remaining);
190                        }
191                        continue;
192                case TRACE_ETHERTYPE_PPP_SES: /* PPPoE */
193                        iphdr = trace_get_payload_from_pppoe(iphdr, ethertype,
194                                        remaining);
195                        continue;
196                default:
197                        break;
198                }
199
200                break;
201        }
202
203        if (!iphdr || *remaining == 0)
204                return NULL;
205
206        /* Store values in the cache for later */
207        /* Cast away constness, nasty, but this is just a cache */
208        ((libtrace_packet_t*)packet)->l3_ethertype = *ethertype;
209        ((libtrace_packet_t*)packet)->l3_header = iphdr;
210
211        return iphdr;
212}
213
214/* parse an ip or tcp option
215 * @param[in,out] ptr   the pointer to the current option
216 * @param[in,out] len   the length of the remaining buffer
217 * @param[out] type     the type of the option
218 * @param[out] optlen   the length of the option
219 * @param[out] data     the data of the option
220 *
221 * @returns bool true if there is another option (and the fields are filled in)
222 *               or false if this was the last option.
223 *
224 * This updates ptr to point to the next option after this one, and updates
225 * len to be the number of bytes remaining in the options area.  Type is updated
226 * to be the code of this option, and data points to the data of this option,
227 * with optlen saying how many bytes there are.
228 *
229 * @note Beware of fragmented packets.
230 * @author Perry Lorier
231 */
232DLLEXPORT int trace_get_next_option(unsigned char **ptr,int *len,
233                        unsigned char *type,
234                        unsigned char *optlen,
235                        unsigned char **data)
236{
237        if (*len<=0)
238                return 0;
239        *type=**ptr;
240        switch(*type) {
241                case 0: /* End of options */
242                        return 0;
243                case 1: /* Pad */
244                        (*ptr)++;
245                        (*len)--;
246                        return 1;
247                default:
248                        *optlen = *(*ptr+1);
249                        if (*optlen<2)
250                                return 0; /* I have no idea wtf is going on
251                                           * with these packets
252                                           */
253
254                        /* Ensure that optlen is not greater than the
255                         * amount of buffer remaining */
256                        if (*optlen > *len) 
257                                return 0;
258                       
259                        (*len)-=*optlen;
260                        (*data)=(*ptr+2);
261                        (*ptr)+=*optlen;
262                        if (*len<0)
263                                return 0;
264                        return 1;
265        }
266        assert(0);
267}
268
269/* Extract the source mac address from a frame and bundle it up into a sockaddr */
270static struct sockaddr *get_source_ethernet_address(
271        libtrace_ether_t *ethernet, struct sockaddr *addr)
272{
273#ifdef HAVE_NETPACKET_PACKET_H
274/* Use linux's sockaddr_ll structure */
275        static struct sockaddr_storage dummy;
276        struct sockaddr_ll *l2addr;
277
278        if (addr)
279                l2addr = (struct sockaddr_ll*)addr;
280        else
281                l2addr = (struct sockaddr_ll*)&dummy;
282       
283        l2addr->sll_family = AF_PACKET;
284        l2addr->sll_protocol = ethernet->ether_type;
285        l2addr->sll_ifindex = 0; /* Irrelevant */
286        l2addr->sll_hatype = ARPHRD_ETHER; 
287        l2addr->sll_pkttype = PACKET_OTHERHOST;
288        l2addr->sll_halen = 6;
289        memcpy(l2addr->sll_addr,ethernet->ether_shost, 6);
290
291        return (struct sockaddr*)l2addr;
292#else
293/* TODO: implement BSD's sockaddr_dl structure, sigh. */
294        return NULL;
295#endif
296}
297
298static struct sockaddr *get_source_l2_address(
299        const libtrace_packet_t *packet, struct sockaddr *addr)
300{
301        static struct sockaddr_storage dummy;
302        void *l2;
303        libtrace_linktype_t linktype;
304        uint32_t remaining;
305
306        if (!addr)
307                addr =(struct sockaddr*)&dummy;
308
309        l2=trace_get_layer2(packet, &linktype, &remaining);
310        if (!l2) {
311                return NULL;
312        }
313
314        switch (linktype) {
315                case TRACE_TYPE_ETH:
316                        return get_source_ethernet_address((libtrace_ether_t*)l2, addr);
317                default:
318                        return NULL;
319        }
320}
321
322DLLEXPORT struct sockaddr *trace_get_source_address(
323                const libtrace_packet_t *packet, struct sockaddr *addr)
324{
325        uint16_t ethertype;
326        uint32_t remaining;
327        void *l3;
328        struct ports_t *ports;
329        static struct sockaddr_storage dummy;
330
331        if (!addr)
332                addr=(struct sockaddr*)&dummy;
333
334        l3 = trace_get_layer3(packet,&ethertype,&remaining);
335
336        if (!l3)
337                return get_source_l2_address(packet,addr);
338
339        switch (ethertype) {
340                case TRACE_ETHERTYPE_IP: /* IPv4 */
341                {
342                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
343                        libtrace_ip_t *ip = (libtrace_ip_t*)l3;
344                        ports = (struct ports_t*)
345                                trace_get_payload_from_ip(ip,NULL,&remaining);
346                        addr4->sin_family=AF_INET;
347                        if (ports && remaining>=sizeof(*ports))
348                                addr4->sin_port=ports->src;
349                        else
350                                addr4->sin_port=0;
351                        addr4->sin_addr=ip->ip_src;
352                        return addr;
353                }
354                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
355                {
356                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
357                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)l3;
358                        ports = (struct ports_t*)
359                                trace_get_payload_from_ip6(ip6,NULL,&remaining);
360                        addr6->sin6_family=AF_INET6;
361                        if (ports && remaining>=sizeof(*ports))
362                                addr6->sin6_port=ports->src;
363                        else
364                                addr6->sin6_port=0;
365                        addr6->sin6_flowinfo=0;
366                        addr6->sin6_addr=ip6->ip_src;
367                        return addr;
368                }
369                default:
370                        return get_source_l2_address(packet, addr);
371        }
372}
373
374
375static struct sockaddr *get_destination_ethernet_address(
376        libtrace_ether_t *ethernet, struct sockaddr *addr)
377{
378#ifdef HAVE_NETPACKET_PACKET_H
379/* Use linux's sockaddr_ll structure */
380        static struct sockaddr_storage dummy;
381        struct sockaddr_ll *l2addr;
382        if (addr)
383                l2addr = (struct sockaddr_ll*)addr;
384        else
385                l2addr = (struct sockaddr_ll*)&dummy;
386       
387        l2addr->sll_family = AF_PACKET;
388        l2addr->sll_protocol = ethernet->ether_type;
389        l2addr->sll_ifindex = 0; /* Irrelevant */
390        l2addr->sll_hatype = ARPHRD_ETHER; 
391        l2addr->sll_pkttype = PACKET_OTHERHOST;
392        l2addr->sll_halen = 6;
393        memcpy(l2addr->sll_addr,ethernet->ether_dhost, 6);
394
395        return (struct sockaddr*)l2addr;
396#else
397/* TODO: implement BSD's sockaddr_dl structure, sigh. */
398        return NULL;
399#endif
400}
401
402static struct sockaddr *get_destination_l2_address(
403        const libtrace_packet_t *packet, struct sockaddr *addr)
404{
405        static struct sockaddr_storage dummy;
406        void *l2;
407        libtrace_linktype_t linktype;
408        uint32_t remaining;
409        if (!addr)
410                addr =(struct sockaddr*)&dummy;
411        l2=trace_get_layer2(packet, &linktype, &remaining);
412        if (!l2)
413                return NULL;
414
415        switch (linktype) {
416                case TRACE_TYPE_ETH:
417                        return get_destination_ethernet_address((libtrace_ether_t*)l2, addr);
418                default:
419                        return NULL;
420        }
421}
422
423DLLEXPORT struct sockaddr *trace_get_destination_address(
424                const libtrace_packet_t *packet, struct sockaddr *addr)
425{
426        uint16_t ethertype;
427        uint32_t remaining;
428        void *l3;
429        struct ports_t *ports;
430        static struct sockaddr_storage dummy;
431
432        if (!addr)
433                addr=(struct sockaddr*)&dummy;
434
435        l3 = trace_get_layer3(packet,&ethertype,&remaining);
436
437        if (!l3)
438                return get_destination_l2_address(packet,addr);
439
440        switch (ethertype) {
441                case TRACE_ETHERTYPE_IP: /* IPv4 */
442                {
443                        struct sockaddr_in *addr4=(struct sockaddr_in*)addr;
444                        libtrace_ip_t *ip = (libtrace_ip_t*)l3;
445                        ports = (struct ports_t*)
446                                trace_get_payload_from_ip(ip,NULL,&remaining);
447                        addr4->sin_family=AF_INET;
448                        if (ports && remaining>=sizeof(*ports))
449                                addr4->sin_port=ports->dst;
450                        else
451                                addr4->sin_port=0;
452                        addr4->sin_addr=ip->ip_dst;
453                        return addr;
454                }
455                case TRACE_ETHERTYPE_IPV6: /* IPv6 */
456                {
457                        struct sockaddr_in6 *addr6=(struct sockaddr_in6*)addr;
458                        libtrace_ip6_t *ip6 = (libtrace_ip6_t*)l3;
459                        ports = (struct ports_t*)
460                                trace_get_payload_from_ip6(ip6,NULL,&remaining);
461                        addr6->sin6_family=AF_INET6;
462                        if (ports && remaining>=sizeof(*ports))
463                                addr6->sin6_port=ports->dst;
464                        else
465                                addr6->sin6_port=0;
466                        addr6->sin6_flowinfo=0;
467                        addr6->sin6_addr=ip6->ip_dst;
468                        return addr;
469                }
470                default:
471                        return get_destination_l2_address(packet, addr);
472        }
473}
474
475
Note: See TracBrowser for help on using the repository browser.