1 /*
2  *  $Id: libnet_pblock.c,v 1.14 2004/11/09 07:05:07 mike Exp $
3  *
4  *  libnet
5  *  libnet_pblock.c - Memory protocol block routines.
6  *
7  *  Copyright (c) 1998 - 2004 Mike D. Schiffman <mike@infonexus.com>
8  *  All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  *
31  */
32 
33 #if (HAVE_CONFIG_H)
34 #include "../include/config.h"
35 #endif
36 #if (!(_WIN32) || (__CYGWIN__))
37 #include "../include/libnet.h"
38 #else
39 #include "../include/win32/libnet.h"
40 #endif
41 #include <assert.h>
42 
43 libnet_pblock_t *
libnet_pblock_probe(libnet_t * l,libnet_ptag_t ptag,uint32_t b_len,uint8_t type)44 libnet_pblock_probe(libnet_t *l, libnet_ptag_t ptag, uint32_t b_len, uint8_t type)
45 {
46     int offset;
47     libnet_pblock_t *p;
48 
49     if (ptag == LIBNET_PTAG_INITIALIZER)
50     {
51         return libnet_pblock_new(l, b_len);
52     }
53 
54     /*
55      *  Update this pblock, don't create a new one.  Note that if the
56      *  new packet size is larger than the old one we will do a malloc.
57      */
58     p = libnet_pblock_find(l, ptag);
59 
60     if (p == NULL)
61     {
62         /* err msg set in libnet_pblock_find() */
63         return (NULL);
64     }
65     if (p->type != type)
66     {
67         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
68                 "%s(): ptag refers to different type than expected (0x%x != 0x%x)",
69                 __func__, p->type, type);
70         return (NULL);
71     }
72     /*
73      *  If size is greater than the original block of memory, we need
74      *  to malloc more memory.  Should we use realloc?
75      */
76     if (b_len > p->b_len)
77     {
78         offset = b_len - p->b_len;  /* how many bytes larger new pblock is */
79         free(p->buf);
80         p->buf = malloc(b_len);
81         if (p->buf == NULL)
82         {
83             snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
84                     "%s(): can't resize pblock buffer: %s\n", __func__,
85                     strerror(errno));
86             return (NULL);
87         }
88         memset(p->buf, 0, b_len);
89         p->h_len += offset; /* new length for checksums */
90         p->b_len = b_len;       /* new buf len */
91         l->total_size += offset;
92     }
93     else
94     {
95         offset = p->b_len - b_len;
96         p->h_len -= offset; /* new length for checksums */
97         p->b_len = b_len;       /* new buf len */
98         l->total_size -= offset;
99     }
100     p->copied = 0;      /* reset copied counter */
101 
102     return (p);
103 }
104 
zmalloc(libnet_t * l,uint32_t size,const char * func)105 static void* zmalloc(libnet_t* l, uint32_t size, const char* func)
106 {
107     void* v = malloc(size);
108     if(v)
109         memset(v, 0, size);
110     else
111         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s\n", func,
112                 strerror(errno));
113     return v;
114 }
115 
116 libnet_pblock_t *
libnet_pblock_new(libnet_t * l,uint32_t b_len)117 libnet_pblock_new(libnet_t *l, uint32_t b_len)
118 {
119     libnet_pblock_t *p = zmalloc(l, sizeof(libnet_pblock_t), __func__);
120     if(!p)
121         return NULL;
122 
123     p->buf = zmalloc(l, b_len, __func__);
124 
125     if(!p->buf)
126     {
127         free(p);
128         return NULL;
129     }
130 
131     p->b_len = b_len;
132 
133     l->total_size += b_len;
134     l->n_pblocks++;
135 
136     /* make the head node if it doesn't exist */
137     if (l->protocol_blocks == NULL)
138     {
139         l->protocol_blocks = p;
140         l->pblock_end = p;
141     }
142     else
143     {
144         l->pblock_end->next = p;
145         p->prev = l->pblock_end;
146         l->pblock_end = p;
147     }
148 
149     return p;
150 }
151 
152 int
libnet_pblock_swap(libnet_t * l,libnet_ptag_t ptag1,libnet_ptag_t ptag2)153 libnet_pblock_swap(libnet_t *l, libnet_ptag_t ptag1, libnet_ptag_t ptag2)
154 {
155     libnet_pblock_t *p1, *p2;
156 
157     p1 = libnet_pblock_find(l, ptag1);
158     p2 = libnet_pblock_find(l, ptag2);
159     if (p1 == NULL || p2 == NULL)
160     {
161         /* error set elsewhere */
162         return (-1);
163     }
164 
165     p2->prev = p1->prev;
166     p1->next = p2->next;
167     p2->next = p1;
168     p1->prev = p2;
169 
170     if (p1->next)
171     {
172         p1->next->prev = p1;
173     }
174 
175     if (p2->prev)
176     {
177         p2->prev->next = p2;
178     }
179     else
180     {
181         /* first node on the list */
182         l->protocol_blocks = p2;
183     }
184 
185     if (l->pblock_end == p2)
186     {
187         l->pblock_end = p1;
188     }
189     return (1);
190 }
191 
libnet_pblock_remove_from_list(libnet_t * l,libnet_pblock_t * p)192 static void libnet_pblock_remove_from_list(libnet_t *l, libnet_pblock_t *p)
193 {
194     if (p->prev)
195     {
196         p->prev->next = p->next;
197     }
198     else
199     {
200         l->protocol_blocks = p->next;
201     }
202 
203     if (p->next)
204     {
205         p->next->prev = p->prev;
206     }
207     else
208     {
209         l->pblock_end = p->prev;
210     }
211 }
212 
213 int
libnet_pblock_insert_before(libnet_t * l,libnet_ptag_t ptag1,libnet_ptag_t ptag2)214 libnet_pblock_insert_before(libnet_t *l, libnet_ptag_t ptag1,
215         libnet_ptag_t ptag2)
216 {
217     libnet_pblock_t *p1, *p2;
218 
219     p1 = libnet_pblock_find(l, ptag1);
220     p2 = libnet_pblock_find(l, ptag2);
221     if (p1 == NULL || p2 == NULL)
222     {
223         /* error set elsewhere */
224         return (-1);
225     }
226 
227     /* check for already present before */
228     if(p2->next == p1)
229         return 1;
230 
231     libnet_pblock_remove_from_list(l, p2);
232 
233     /* insert p2 into list */
234     p2->prev = p1->prev;
235     p2->next = p1;
236     p1->prev = p2;
237 
238     if (p2->prev)
239     {
240         p2->prev->next = p2;
241     }
242     else
243     {
244         /* first node on the list */
245         l->protocol_blocks = p2;
246     }
247 
248     return (1);
249 }
250 
251 libnet_pblock_t *
libnet_pblock_find(libnet_t * l,libnet_ptag_t ptag)252 libnet_pblock_find(libnet_t *l, libnet_ptag_t ptag)
253 {
254     libnet_pblock_t *p;
255 
256     for (p = l->protocol_blocks; p; p = p->next)
257     {
258         if (p->ptag == ptag)
259         {
260             return (p);
261         }
262     }
263     snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
264             "%s(): couldn't find protocol block\n", __func__);
265     return (NULL);
266 }
267 
268 int
libnet_pblock_append(libnet_t * l,libnet_pblock_t * p,const void * buf,uint32_t len)269 libnet_pblock_append(libnet_t *l, libnet_pblock_t *p, const void *buf, uint32_t len)
270 {
271     if (len && !buf)
272     {
273         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
274 			    "%s(): payload inconsistency\n", __func__);
275         return -1;
276     }
277 
278     if (p->copied + len > p->b_len)
279     {
280         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
281                 "%s(): memcpy would overflow buffer\n", __func__);
282         return (-1);
283     }
284     memcpy(p->buf + p->copied, buf, len);
285     p->copied += len;
286     return (1);
287 }
288 
289 void
libnet_pblock_setflags(libnet_pblock_t * p,uint8_t flags)290 libnet_pblock_setflags(libnet_pblock_t *p, uint8_t flags)
291 {
292     p->flags = flags;
293 }
294 
295 /* FIXME both ptag setting and end setting should be done in pblock new and/or pblock probe. */
296 libnet_ptag_t
libnet_pblock_update(libnet_t * l,libnet_pblock_t * p,uint32_t h_len,uint8_t type)297 libnet_pblock_update(libnet_t *l, libnet_pblock_t *p, uint32_t h_len, uint8_t type)
298 {
299     p->type  =  type;
300     p->ptag  =  ++(l->ptag_state);
301     p->h_len = h_len;
302     l->pblock_end = p;              /* point end of pblock list here */
303 
304     return (p->ptag);
305 }
306 
pblock_is_ip(libnet_pblock_t * p)307 static int pblock_is_ip(libnet_pblock_t* p)
308 {
309     return p->type == LIBNET_PBLOCK_IPV4_H || p->type == LIBNET_PBLOCK_IPV6_H;
310 }
311 
312 /* q is either an ip hdr, or is followed  by an ip hdr. return the offset
313  * from end of packet. if there is no offset, we'll return the total size,
314  * and things will break later
315  */
calculate_ip_offset(libnet_t * l,libnet_pblock_t * q)316 static int calculate_ip_offset(libnet_t* l, libnet_pblock_t* q)
317 {
318     int ip_offset = 0;
319     libnet_pblock_t* p = l->protocol_blocks;
320     for(; p && p != q; p = p->next) {
321 	ip_offset += p->b_len;
322     }
323     assert(p == q); /* if not true, then q is not a pblock! */
324 
325     for(; p; p = p->next) {
326 	ip_offset += p->b_len;
327 	if(pblock_is_ip(p))
328 	    break;
329     }
330 
331     return ip_offset;
332 }
333 
334 int
libnet_pblock_coalesce(libnet_t * l,uint8_t ** packet,uint32_t * size)335 libnet_pblock_coalesce(libnet_t *l, uint8_t **packet, uint32_t *size)
336 {
337     /*
338      *  Determine the offset required to keep memory aligned (strict
339      *  architectures like solaris enforce this, but's a good practice
340      *  either way).  This is only required on the link layer with the
341      *  14 byte ethernet offset (others are similarly unkind).
342      */
343     if (l->injection_type == LIBNET_LINK ||
344         l->injection_type == LIBNET_LINK_ADV)
345     {
346         /* 8 byte alignment should work */
347         l->aligner = 8 - (l->link_offset % 8);
348     }
349     else
350     {
351         l->aligner = 0;
352     }
353 
354     if(!l->total_size && !l->aligner) {
355         /* Avoid allocating zero bytes of memory, it perturbs electric fence. */
356         *packet = malloc(1);
357         **packet =1;
358     } else {
359         *packet = malloc(l->aligner + l->total_size);
360     }
361     if (*packet == NULL)
362     {
363         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE, "%s(): malloc(): %s\n",
364                 __func__, strerror(errno));
365         return (-1);
366     }
367 
368     memset(*packet, 0, l->aligner + l->total_size);
369 
370     if (l->injection_type == LIBNET_RAW4 &&
371         l->pblock_end->type == LIBNET_PBLOCK_IPV4_H)
372     {
373         libnet_pblock_setflags(l->pblock_end, LIBNET_PBLOCK_DO_CHECKSUM);
374     }
375 
376     /* additional sanity checks to perform if we're not in advanced mode */
377     if (!(l->injection_type & LIBNET_ADV_MASK))
378     {
379     	switch (l->injection_type)
380     	{
381             case LIBNET_LINK:
382                 if ((l->pblock_end->type != LIBNET_PBLOCK_TOKEN_RING_H) &&
383                     (l->pblock_end->type != LIBNET_PBLOCK_FDDI_H)       &&
384                     (l->pblock_end->type != LIBNET_PBLOCK_ETH_H)        &&
385                     (l->pblock_end->type != LIBNET_PBLOCK_802_1Q_H)     &&
386                     (l->pblock_end->type != LIBNET_PBLOCK_ISL_H)        &&
387                     (l->pblock_end->type != LIBNET_PBLOCK_802_3_H))
388                 {
389                     snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
390                     "%s(): packet assembly cannot find a layer 2 header\n",
391                     __func__);
392                     goto err;
393                 }
394                 break;
395             case LIBNET_RAW4:
396                 if ((l->pblock_end->type != LIBNET_PBLOCK_IPV4_H))
397                 {
398                     snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
399                     "%s(): packet assembly cannot find an IPv4 header\n",
400                      __func__);
401                     goto err;
402                 }
403                 break;
404             case LIBNET_RAW6:
405                 if ((l->pblock_end->type != LIBNET_PBLOCK_IPV6_H))
406                 {
407                     snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
408                     "%s(): packet assembly cannot find an IPv6 header\n",
409                      __func__);
410                     goto err;
411                 }
412                 break;
413             default:
414                 /* we should not end up here ever */
415                 snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
416                 "%s(): suddenly the dungeon collapses -- you die\n",
417                  __func__);
418                 goto err;
419             break;
420         }
421     }
422 
423     /* Build packet from end to start. */
424     {
425         /*
426            From top to bottom, go through pblocks pairwise:
427 
428            p   is the currently being copied pblock, and steps through every block
429            q   is the prev pblock to p that needs checksumming, it will
430                not step through every block as p does, it will skip any that do not
431                need checksumming.
432            n   offset from start of packet to beginning of block we are writing
433 
434            q is NULL on first iteration
435            p is NULL on last iteration
436 
437            Checksums are done on q, to give p a chance to be copied over, since
438            checksumming q can require a lower-level header to be encoded, in the
439            case of IP protocols (which are the only kinds handled by libnet's
440            checksum implementation).
441 
442            This is very obscure, or would be much more clear if it was done in
443            two loops.
444            */
445         libnet_pblock_t *q = NULL;
446         libnet_pblock_t *p = NULL;
447         uint32_t n;
448 
449         for (n = l->aligner + l->total_size, p = l->protocol_blocks; p || q; )
450         {
451             if (q)
452             {
453                 p = p->next;
454             }
455             if (p)
456             {
457                 n -= p->b_len;
458                 /* copy over the packet chunk */
459                 memcpy(*packet + n, p->buf, p->b_len);
460             }
461 #if 0
462             printf("-- n %d/%d cksum? %d\n", n, l->aligner + l->total_size,
463                     q &&
464                     (p == NULL || (p->flags & LIBNET_PBLOCK_DO_CHECKSUM)) &&
465                     (q->flags & LIBNET_PBLOCK_DO_CHECKSUM));
466             if(q)
467             {
468                 printf(" iph %d/%d offset -%d\n",
469                         (l->total_size + l->aligner) - q->ip_offset,
470                         l->total_size + l->aligner,
471                         q->ip_offset
472                       );
473             }
474             if (p)
475             {
476                 printf("p %p ptag %d b_len %d h_len %d cksum? %d type %s\n",
477                         p, p->ptag,
478                         p->b_len, p->h_len,
479                         p->flags & LIBNET_PBLOCK_DO_CHECKSUM,
480                         libnet_diag_dump_pblock_type(p->type)
481                       );
482             }
483             if (q)
484             {
485                 printf("q %p ptag %d b_len %d h_len %d cksum? %d type %s\n",
486                         q, q->ptag,
487                         q->b_len, q->h_len,
488                         q->flags & LIBNET_PBLOCK_DO_CHECKSUM,
489                         libnet_diag_dump_pblock_type(q->type)
490                       );
491             }
492 #endif
493             if (q)
494             {
495                 if (p == NULL || (p->flags & LIBNET_PBLOCK_DO_CHECKSUM))
496                 {
497                     if (q->flags & LIBNET_PBLOCK_DO_CHECKSUM)
498                     {
499                         uint32_t c;
500                         uint8_t* end = *packet + l->aligner + l->total_size;
501                         uint8_t* beg = *packet + n;
502                         int ip_offset = calculate_ip_offset(l, q);
503                         uint8_t* iph = end - ip_offset;
504 #if 0
505 			printf("p %d/%s q %d/%s offset calculated %d\n",
506 				p ? p->ptag : -1, p ? libnet_diag_dump_pblock_type(p->type) : "nil",
507 				q->ptag, libnet_diag_dump_pblock_type(q->type),
508 				ip_offset);
509 #endif
510                         c = libnet_inet_checksum(l, iph,
511                                 libnet_pblock_p2p(q->type), q->h_len,
512                                 beg, end);
513                         if (c == -1)
514                         {
515                             /* err msg set in libnet_do_checksum() */
516                             goto err;
517                         }
518                     }
519                     q = p;
520                 }
521             }
522             else
523             {
524                 q = p;
525             }
526         }
527     }
528     *size = l->aligner + l->total_size;
529 
530     /*
531      *  Set the packet pointer to the true beginning of the packet and set
532      *  the size for transmission.
533      */
534     if ((l->injection_type == LIBNET_LINK ||
535         l->injection_type == LIBNET_LINK_ADV) && l->aligner)
536     {
537         *packet += l->aligner;
538         *size -= l->aligner;
539     }
540     return (1);
541 
542 err:
543     free(*packet);
544     *packet = NULL;
545     return (-1);
546 }
547 
548 void
libnet_pblock_delete(libnet_t * l,libnet_pblock_t * p)549 libnet_pblock_delete(libnet_t *l, libnet_pblock_t *p)
550 {
551     if (p)
552     {
553         l->total_size -= p->b_len;
554         l->n_pblocks--;
555 
556         libnet_pblock_remove_from_list(l, p);
557 
558         if (p->buf)
559         {
560             free(p->buf);
561             p->buf = NULL;
562         }
563 
564         free(p);
565     }
566 }
567 
568 int
libnet_pblock_p2p(uint8_t type)569 libnet_pblock_p2p(uint8_t type)
570 {
571     /* for checksum; return the protocol number given a pblock type*/
572     switch (type)
573     {
574         case LIBNET_PBLOCK_CDP_H:
575             return (LIBNET_PROTO_CDP);
576         case LIBNET_PBLOCK_ICMPV4_H:
577         case LIBNET_PBLOCK_ICMPV4_ECHO_H:
578         case LIBNET_PBLOCK_ICMPV4_MASK_H:
579         case LIBNET_PBLOCK_ICMPV4_UNREACH_H:
580         case LIBNET_PBLOCK_ICMPV4_TIMXCEED_H:
581         case LIBNET_PBLOCK_ICMPV4_REDIRECT_H:
582         case LIBNET_PBLOCK_ICMPV4_TS_H:
583             return (IPPROTO_ICMP);
584         case LIBNET_PBLOCK_ICMPV6_H:
585         case LIBNET_PBLOCK_ICMPV6_ECHO_H:
586         case LIBNET_PBLOCK_ICMPV6_UNREACH_H:
587         case LIBNET_PBLOCK_ICMPV6_NDP_NSOL_H:
588         case LIBNET_PBLOCK_ICMPV6_NDP_NADV_H:
589             return (IPPROTO_ICMPV6);
590         case LIBNET_PBLOCK_IGMP_H:
591             return (IPPROTO_IGMP);
592         case LIBNET_PBLOCK_IPV4_H:
593             return (IPPROTO_IP);
594         case LIBNET_PBLOCK_IPV6_H:
595             return (IPPROTO_IPV6);
596         case LIBNET_ISL_H:
597             return (LIBNET_PROTO_ISL);
598         case LIBNET_PBLOCK_OSPF_H:
599             return (IPPROTO_OSPF);
600         case LIBNET_PBLOCK_LS_RTR_H:
601             return (IPPROTO_OSPF_LSA);
602         case LIBNET_PBLOCK_TCP_H:
603             return (IPPROTO_TCP);
604         case LIBNET_PBLOCK_UDP_H:
605             return (IPPROTO_UDP);
606         case LIBNET_PBLOCK_VRRP_H:
607             return (IPPROTO_VRRP);
608         case LIBNET_PBLOCK_GRE_H:
609             return (IPPROTO_GRE);
610         default:
611             return (-1);
612     }
613 }
614 
615 void
libnet_pblock_record_ip_offset(libnet_t * l,libnet_pblock_t * p)616 libnet_pblock_record_ip_offset(libnet_t *l, libnet_pblock_t *p)
617 {
618     (void) l;
619     (void) p;
620     /* For backwards compatibility, libnet_pblock_t no longer includes
621        an ip_offset, so calling this is unnecessary.
622        */
623 }
624 
625 
626