source: lib/protocols_l3.c @ 15e9390

4.0.1-hotfixescachetimestampsdevelopdpdk-ndagetsilivegetfragoffhelplibtrace4ndag_formatpfringrc-4.0.1rc-4.0.2rc-4.0.3rc-4.0.4ringdecrementfixringperformanceringtimestampfixes
Last change on this file since 15e9390 was 59751a5, checked in by Perry Lorier <perry@…>, 12 years ago

have trace_get_source_address() / trace_get_destination_address() return link layer addresses
where possible.

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