1 /*
2  *  $Id: libnet_build_ip.c,v 1.19 2004/04/13 17:32:28 mike Exp $
3  *
4  *  libnet
5  *  libnet_build_ip.c - IP packet assembler
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 
42 
43 /* TODO len - should be calculated if -1 */
44 libnet_ptag_t
libnet_build_ipv4(uint16_t ip_len,uint8_t tos,uint16_t id,uint16_t frag,uint8_t ttl,uint8_t prot,uint16_t sum,uint32_t src,uint32_t dst,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)45 libnet_build_ipv4(uint16_t ip_len, uint8_t tos, uint16_t id, uint16_t frag,
46 uint8_t ttl, uint8_t prot, uint16_t sum, uint32_t src, uint32_t dst,
47 const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
48 {
49     uint32_t n = LIBNET_IPV4_H; /* size of memory block */
50     libnet_pblock_t *p, *p_data, *p_temp;
51     struct libnet_ipv4_hdr ip_hdr;
52     libnet_ptag_t ptag_data = 0; /* used if there is ipv4 payload */
53     libnet_ptag_t ptag_hold;
54 
55     if (l == NULL)
56     {
57         return (-1);
58     }
59 
60     /*
61      *  Find the existing protocol block if a ptag is specified, or create
62      *  a new one.
63      */
64     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV4_H);
65     if (p == NULL)
66     {
67         return (-1);
68     }
69 
70     memset(&ip_hdr, 0, sizeof(ip_hdr));
71     ip_hdr.ip_v          = 4;      /* version 4 */
72     ip_hdr.ip_hl         = 5;      /* 20 byte header,  measured in 32-bit words */
73 
74     /* check to see if there are IP options to include */
75     if (p->prev)
76     {
77         if (p->prev->type == LIBNET_PBLOCK_IPO_H)
78         {
79             /* IPO block's length must be multiple of 4, or it's incorrectly
80              * padded, in which case there is no "correct" IP header length,
81              * it will too short or too long, we choose too short.
82              */
83             ip_hdr.ip_hl += p->prev->b_len / 4;
84         }
85     }
86     /* Note that p->h_len is not adjusted. This seems a bug, but it is because
87      * it is not used!  libnet_do_checksum() is passed the h_len (as `len'),
88      * but for IPPROTO_IP it is ignored in favor of the ip_hl.
89      */
90 
91     ip_hdr.ip_tos        = tos;                       /* IP tos */
92     ip_hdr.ip_len        = htons(ip_len);             /* total length */
93     ip_hdr.ip_id         = htons(id);                 /* IP ID */
94     ip_hdr.ip_off        = htons(frag);               /* fragmentation flags */
95     ip_hdr.ip_ttl        = ttl;                       /* time to live */
96     ip_hdr.ip_p          = prot;                      /* transport protocol */
97     ip_hdr.ip_sum        = (sum ? htons(sum) : 0);    /* checksum */
98     ip_hdr.ip_src.s_addr = src;                       /* source ip */
99     ip_hdr.ip_dst.s_addr = dst;                       /* destination ip */
100 
101     n = libnet_pblock_append(l, p, (uint8_t *)&ip_hdr, LIBNET_IPV4_H);
102     if (n == -1)
103     {
104         goto bad;
105     }
106 
107     /* save the original ptag value */
108     ptag_hold = ptag;
109 
110     if (ptag == LIBNET_PTAG_INITIALIZER)
111     {
112         ptag = libnet_pblock_update(l, p, LIBNET_IPV4_H, LIBNET_PBLOCK_IPV4_H);
113     }
114 
115     /* find and set the appropriate ptag, or else use the default of 0 */
116     /* When updating the ipv4 block, we need to find the data block, and
117      * adjust our ip_offset if the new payload size is different from what
118      * it used to be.
119      */
120     if (ptag_hold && p->prev)
121     {
122         p_temp = p->prev;
123         while (p_temp->prev &&
124               (p_temp->type != LIBNET_PBLOCK_IPDATA) &&
125               (p_temp->type != LIBNET_PBLOCK_IPV4_H))
126         {
127             p_temp = p_temp->prev;
128         }
129 
130         if (p_temp->type == LIBNET_PBLOCK_IPDATA)
131         {
132             ptag_data = p_temp->ptag;
133         }
134         else
135         {
136              snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
137                      "%s(): IPv4 data pblock not found\n", __func__);
138         }
139     }
140 
141     if (payload_s && !payload)
142     {
143          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
144                  "%s(): payload inconsistency\n", __func__);
145         goto bad;
146     }
147 
148     if (payload_s)
149     {
150         /* update ptag_data with the new payload */
151         /* on create:
152          *    b_len = payload_s
153          *    l->total_size += b_len
154          *    h_len = 0
155          * on update:
156          *    b_len = payload_s
157          *    h_len += <diff in size between new b_len and old b_len>
158          *      increments if if b_len goes up, down if it goes down
159          * in either case:
160          *    copied = 0
161 	 */
162         p_data = libnet_pblock_probe(l, ptag_data, payload_s,
163                 LIBNET_PBLOCK_IPDATA);
164         if (p_data == NULL)
165         {
166             return (-1);
167         }
168 
169         if (libnet_pblock_append(l, p_data, payload, payload_s) == -1)
170         {
171             goto bad;
172         }
173 
174         if (ptag_data == LIBNET_PTAG_INITIALIZER)
175         {
176             /* IPDATA's h_len gets set to payload_s in both branches */
177             if (p_data->prev->type == LIBNET_PBLOCK_IPV4_H)
178             {
179                 libnet_pblock_update(l, p_data, payload_s,
180                         LIBNET_PBLOCK_IPDATA);
181                 /* swap pblocks to correct the protocol order */
182                 libnet_pblock_swap(l, p->ptag, p_data->ptag);
183             }
184             else
185             {
186                 /* SR - I'm not sure how to reach this code. Maybe if the first
187                  * time we added an ipv4 block, there was no payload, but when
188                  * we modify the block the next time, we have payload?
189 		 */
190 
191                 /* update without setting this as the final pblock */
192                 p_data->type  =  LIBNET_PBLOCK_IPDATA;
193                 p_data->ptag  =  ++(l->ptag_state);
194                 p_data->h_len =  payload_s; /* TODO dead code, data blocks don't have headers */
195 
196                 /* data was added after the initial construction */
197                 for (p_temp = l->protocol_blocks;
198                         p_temp->type == LIBNET_PBLOCK_IPV4_H ||
199                         p_temp->type == LIBNET_PBLOCK_IPO_H;
200                         p_temp = p_temp->next)
201                 {
202                     libnet_pblock_insert_before(l, p_temp->ptag, p_data->ptag);
203                     break;
204                 }
205 
206                 /* the end block needs to have its next pointer cleared */
207                 l->pblock_end->next = NULL;
208             }
209 
210             if (p_data->prev && p_data->prev->type == LIBNET_PBLOCK_IPO_H)
211             {
212                 libnet_pblock_swap(l, p_data->prev->ptag, p_data->ptag);
213             }
214         }
215     }
216     else
217     {
218         p_data = libnet_pblock_find(l, ptag_data);
219         if (p_data)
220         {
221             libnet_pblock_delete(l, p_data);
222         }
223         else
224         {
225             /*
226              * XXX - When this completes successfully, libnet errbuf contains
227              * an error message so to come correct, we'll clear it.
228              */
229             memset(l->err_buf, 0, sizeof (l->err_buf));
230         }
231     }
232     if (sum == 0)
233     {
234         /*
235          *  If checksum is zero, by default libnet will compute a checksum
236          *  for the user.  The programmer can override this by calling
237          *  libnet_toggle_checksum(l, ptag, 1);
238          */
239         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
240     }
241 
242     return (ptag);
243 bad:
244     libnet_pblock_delete(l, p);
245     return (-1);
246 }
247 
248 libnet_ptag_t
libnet_autobuild_ipv4(uint16_t len,uint8_t prot,uint32_t dst,libnet_t * l)249 libnet_autobuild_ipv4(uint16_t len, uint8_t prot, uint32_t dst, libnet_t *l)
250 {
251     uint32_t n, i, j, src;
252     uint16_t h;
253     libnet_pblock_t *p;
254     libnet_ptag_t ptag;
255     struct libnet_ipv4_hdr ip_hdr;
256 
257     if (l == NULL)
258     {
259         return (-1);
260     }
261 
262     n = LIBNET_IPV4_H;                                /* size of memory block */
263     h = len;                                          /* header length */
264     ptag = LIBNET_PTAG_INITIALIZER;
265     src = libnet_get_ipaddr4(l);
266     if (src == -1)
267     {
268         /* err msg set in libnet_get_ipaddr() */
269         return (-1);
270     }
271 
272     /*
273      *  Create a new pblock.
274      */
275     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV4_H);
276     if (p == NULL)
277     {
278         return (-1);
279     }
280 
281     memset(&ip_hdr, 0, sizeof(ip_hdr));
282     ip_hdr.ip_v          = 4;                         /* version 4 */
283     ip_hdr.ip_hl         = 5;                         /* 20 byte header */
284 
285     /* check to see if there are IP options to include */
286     if (p->prev)
287     {
288         if (p->prev->type == LIBNET_PBLOCK_IPO_H)
289         {
290             /*
291              *  Count up number of 32-bit words in options list, padding if
292              *  neccessary.
293              */
294             for (i = 0, j = 0; i < p->prev->b_len; i++)
295             {
296                 (i % 4) ? j : j++;
297             }
298             ip_hdr.ip_hl += j;
299         }
300     }
301 
302     ip_hdr.ip_tos        = 0;                         /* IP tos */
303     ip_hdr.ip_len        = htons(h);                  /* total length */
304     ip_hdr.ip_id         = htons((l->ptag_state) & 0x0000ffff); /* IP ID */
305     ip_hdr.ip_off        = 0;                         /* fragmentation flags */
306     ip_hdr.ip_ttl        = 64;                        /* time to live */
307     ip_hdr.ip_p          = prot;                      /* transport protocol */
308     ip_hdr.ip_sum        = 0;                         /* checksum */
309     ip_hdr.ip_src.s_addr = src;                       /* source ip */
310     ip_hdr.ip_dst.s_addr = dst;                       /* destination ip */
311 
312     n = libnet_pblock_append(l, p, (uint8_t *)&ip_hdr, LIBNET_IPV4_H);
313     if (n == -1)
314     {
315         goto bad;
316     }
317 
318     libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
319     ptag = libnet_pblock_update(l, p, LIBNET_IPV4_H, LIBNET_PBLOCK_IPV4_H);
320 
321     return (ptag);
322 
323 bad:
324     libnet_pblock_delete(l, p);
325     return (-1);
326 }
327 
328 libnet_ptag_t
libnet_build_ipv4_options(const uint8_t * options,uint32_t options_s,libnet_t * l,libnet_ptag_t ptag)329 libnet_build_ipv4_options(const uint8_t *options, uint32_t options_s, libnet_t *l,
330 libnet_ptag_t ptag)
331 {
332     int options_size_increase = 0; /* increase will be negative if it's a decrease */
333     uint32_t n, adj_size;
334     libnet_pblock_t *p, *p_temp;
335     struct libnet_ipv4_hdr *ip_hdr;
336 
337     if (l == NULL)
338     {
339         return (-1);
340     }
341 
342     /* check options list size */
343     if (options_s > LIBNET_MAXOPTION_SIZE)
344     {
345         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
346             "%s(): options list is too large %d\n", __func__, options_s);
347         return (-1);
348     }
349 
350     adj_size = options_s;
351     if (adj_size % 4)
352     {
353         /* size of memory block with padding */
354         adj_size += 4 - (options_s % 4);
355     }
356 
357     /* if this pblock already exists, determine if there is a size diff */
358     if (ptag)
359     {
360         p_temp = libnet_pblock_find(l, ptag);
361         if (p_temp)
362         {
363             options_size_increase = adj_size - p_temp->b_len;
364         }
365     }
366     /* If we aren't modifying an options block, we are pushing a new one, and
367      * since it must be pushed before the IPv4 block is pushed, there is no
368      * need to remember that options size has "increased".
369      */
370 
371     /*
372      *  Find the existing protocol block if a ptag is specified, or create
373      *  a new one.
374      */
375     p = libnet_pblock_probe(l, ptag, adj_size, LIBNET_PBLOCK_IPO_H);
376     if (p == NULL)
377     {
378         return (-1);
379     }
380 
381     /* append options */
382     n = libnet_pblock_append(l, p, options, options_s);
383     if (n == -1)
384     {
385         goto bad;
386     }
387 
388     /* append padding */
389     n = libnet_pblock_append(l, p, (uint8_t*)"\0\0\0", adj_size - options_s);
390     if (n == -1)
391     {
392         goto bad;
393     }
394 
395     if (ptag && p->next)
396     {
397         p_temp = p->next;
398 
399         /* fix the IP header sizes */
400         if (p_temp->type == LIBNET_PBLOCK_IPV4_H)
401         {
402             ip_hdr = (struct libnet_ipv4_hdr *) p_temp->buf;
403             ip_hdr->ip_hl = 5 + adj_size / 4; /* 4 bits wide, so no byte order concerns */
404             ip_hdr->ip_len = htons(ntohs(ip_hdr->ip_len) + options_size_increase);
405 
406             p_temp->h_len = ip_hdr->ip_hl * 4; /* Dead code, h_len isn't used for IPv4 block */
407         }
408     }
409 
410     return (ptag ? ptag : libnet_pblock_update(l, p, adj_size,
411             LIBNET_PBLOCK_IPO_H));
412 bad:
413     libnet_pblock_delete(l, p);
414     return (-1);
415 }
416 
417 libnet_ptag_t
libnet_build_ipv6(uint8_t tc,uint32_t fl,uint16_t len,uint8_t nh,uint8_t hl,struct libnet_in6_addr src,struct libnet_in6_addr dst,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)418 libnet_build_ipv6(uint8_t tc, uint32_t fl, uint16_t len, uint8_t nh,
419 uint8_t hl, struct libnet_in6_addr src, struct libnet_in6_addr dst,
420 const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
421 {
422     uint32_t n;
423     libnet_pblock_t *p;
424     struct libnet_ipv6_hdr ip_hdr;
425 
426     if (l == NULL)
427     {
428         return (-1);
429     }
430 
431     n = LIBNET_IPV6_H + payload_s;          /* size of memory block */
432 
433     if (LIBNET_IPV6_H + payload_s > IP_MAXPACKET)
434     {
435          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
436                  "%s(): IP packet too large\n", __func__);
437         return (-1);
438     }
439 
440     /*
441      *  Find the existing protocol block if a ptag is specified, or create
442      *  a new one.
443      */
444     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV6_H);
445     if (p == NULL)
446     {
447         return (-1);
448     }
449 
450     memset(&ip_hdr, 0, sizeof(ip_hdr));
451     ip_hdr.ip_flags[0] = (0x06 << 4) | ((tc & 0xF0) >> 4);
452     ip_hdr.ip_flags[1] = ((tc & 0x0F) << 4) | ((fl & 0xF0000) >> 16);
453     ip_hdr.ip_flags[2] = fl & 0x0FF00 >> 8;
454     ip_hdr.ip_flags[3] = fl & 0x000FF;
455     ip_hdr.ip_len      = htons(len);
456     ip_hdr.ip_nh       = nh;
457     ip_hdr.ip_hl       = hl;
458     ip_hdr.ip_src      = src;
459     ip_hdr.ip_dst      = dst;
460 
461     n = libnet_pblock_append(l, p, (uint8_t *)&ip_hdr, LIBNET_IPV6_H);
462     if (n == -1)
463     {
464         goto bad;
465     }
466 
467     /* boilerplate payload sanity check / append macro */
468     LIBNET_DO_PAYLOAD(l, p);
469 
470     /* no checksum for IPv6 */
471     ptag = ptag ? ptag : libnet_pblock_update(l, p, LIBNET_IPV6_H,
472             LIBNET_PBLOCK_IPV6_H);
473 
474     return ptag;
475 
476 bad:
477     libnet_pblock_delete(l, p);
478     return (-1);
479 }
480 
481 libnet_ptag_t
libnet_build_ipv6_frag(uint8_t nh,uint8_t reserved,uint16_t frag,uint32_t id,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)482 libnet_build_ipv6_frag(uint8_t nh, uint8_t reserved, uint16_t frag,
483 uint32_t id, const uint8_t *payload, uint32_t payload_s, libnet_t *l,
484 libnet_ptag_t ptag)
485 {
486     uint32_t n;
487     uint16_t h;
488     libnet_pblock_t *p;
489     struct libnet_ipv6_frag_hdr ipv6_frag_hdr;
490 
491     if (l == NULL)
492     {
493         return (-1);
494     }
495 
496     n = LIBNET_IPV6_FRAG_H + payload_s;
497     h = 0;
498 
499     if (LIBNET_IPV6_FRAG_H + payload_s > IP_MAXPACKET)
500     {
501          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
502                  "%s(): IP packet too large\n", __func__);
503         return (-1);
504     }
505 
506     /*
507      *  Find the existing protocol block if a ptag is specified, or create
508      *  a new one.
509      */
510     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV6_FRAG_H);
511     if (p == NULL)
512     {
513         return (-1);
514     }
515 
516     memset(&ipv6_frag_hdr, 0 , sizeof(ipv6_frag_hdr));
517     ipv6_frag_hdr.ip_nh       = nh;
518     ipv6_frag_hdr.ip_reserved = reserved;
519     ipv6_frag_hdr.ip_frag     = frag;
520     ipv6_frag_hdr.ip_id       = id;
521 
522     /*
523      *  Appened the protocol unit to the list.
524      */
525     n = libnet_pblock_append(l, p, (uint8_t *)&ipv6_frag_hdr,
526         LIBNET_IPV6_FRAG_H);
527     if (n == -1)
528     {
529         goto bad;
530     }
531 
532     /* boilerplate payload sanity check / append macro */
533     LIBNET_DO_PAYLOAD(l, p);
534 
535     /*
536      *  Update the protocol block's meta information and return the protocol
537      *  tag id of this pblock.  This tag will be used to locate the pblock
538      *  in order to modify the protocol header in subsequent calls.
539      */
540     return (ptag ? ptag : libnet_pblock_update(l, p, h,
541             LIBNET_PBLOCK_IPV6_FRAG_H));
542 bad:
543     libnet_pblock_delete(l, p);
544     return (-1);
545 }
546 
547 libnet_ptag_t
libnet_build_ipv6_routing(uint8_t nh,uint8_t len,uint8_t rtype,uint8_t segments,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)548 libnet_build_ipv6_routing(uint8_t nh, uint8_t len, uint8_t rtype,
549 uint8_t segments, const uint8_t *payload, uint32_t payload_s, libnet_t *l,
550 libnet_ptag_t ptag)
551 {
552     uint32_t n;
553     uint16_t h;
554     libnet_pblock_t *p;
555     struct libnet_ipv6_routing_hdr ipv6_routing_hdr;
556 
557     if (l == NULL)
558     {
559         return (-1);
560     }
561 
562     /* Important: IPv6 routing header routes are specified using the payload
563      * interface!
564      */
565     n = LIBNET_IPV6_ROUTING_H + payload_s;
566     h = 0;
567 
568     if (LIBNET_IPV6_ROUTING_H + payload_s > IP_MAXPACKET)
569     {
570          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
571                  "%s(): IP packet too large\n", __func__);
572         return (-1);
573     }
574 
575     /*
576      *  Find the existing protocol block if a ptag is specified, or create
577      *  a new one.
578      */
579     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV6_ROUTING_H);
580     if (p == NULL)
581     {
582         return (-1);
583     }
584 
585     memset(&ipv6_routing_hdr, 0 , sizeof(ipv6_routing_hdr));
586     ipv6_routing_hdr.ip_nh       = nh;
587     ipv6_routing_hdr.ip_len      = len;
588     ipv6_routing_hdr.ip_rtype    = rtype;
589     ipv6_routing_hdr.ip_segments = segments;
590 
591     /*
592      *  Appened the protocol unit to the list.
593      */
594     n = libnet_pblock_append(l, p, (uint8_t *)&ipv6_routing_hdr,
595         LIBNET_IPV6_ROUTING_H);
596     if (n == -1)
597     {
598         goto bad;
599     }
600 
601     /* boilerplate payload sanity check / append macro */
602     LIBNET_DO_PAYLOAD(l, p);
603 
604     /*
605      *  Update the protocol block's meta information and return the protocol
606      *  tag id of this pblock.  This tag will be used to locate the pblock
607      *  in order to modify the protocol header in subsequent calls.
608      */
609     return (ptag ? ptag : libnet_pblock_update(l, p, h,
610             LIBNET_PBLOCK_IPV6_ROUTING_H));
611 bad:
612     libnet_pblock_delete(l, p);
613     return (-1);
614 }
615 
616 libnet_ptag_t
libnet_build_ipv6_destopts(uint8_t nh,uint8_t len,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)617 libnet_build_ipv6_destopts(uint8_t nh, uint8_t len, const uint8_t *payload,
618 uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
619 {
620     uint32_t n;
621     uint16_t h;
622     libnet_pblock_t *p;
623     struct libnet_ipv6_destopts_hdr ipv6_destopts_hdr;
624 
625     if (l == NULL)
626     {
627         return (-1);
628     }
629 
630     /* Important: IPv6 dest opts information is specified using the payload
631      * interface!
632      */
633     n = LIBNET_IPV6_DESTOPTS_H + payload_s;
634     h = 0;
635 
636     if (LIBNET_IPV6_DESTOPTS_H + payload_s > IP_MAXPACKET)
637     {
638          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
639                  "%s(): IP packet too large\n", __func__);
640         return (-1);
641     }
642 
643     /*
644      *  Find the existing protocol block if a ptag is specified, or create
645      *  a new one.
646      */
647     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV6_DESTOPTS_H);
648     if (p == NULL)
649     {
650         return (-1);
651     }
652 
653     memset(&ipv6_destopts_hdr, 0 , sizeof(ipv6_destopts_hdr));
654     ipv6_destopts_hdr.ip_nh  = nh;
655     ipv6_destopts_hdr.ip_len = len;
656 
657     /*
658      *  Appened the protocol unit to the list.
659      */
660     n = libnet_pblock_append(l, p, (uint8_t *)&ipv6_destopts_hdr,
661         LIBNET_IPV6_DESTOPTS_H);
662     if (n == -1)
663     {
664         goto bad;
665     }
666 
667     /* boilerplate payload sanity check / append macro */
668     LIBNET_DO_PAYLOAD(l, p);
669 
670     /*
671      *  Update the protocol block's meta information and return the protocol
672      *  tag id of this pblock.  This tag will be used to locate the pblock
673      *  in order to modify the protocol header in subsequent calls.
674      */
675     return (ptag ? ptag : libnet_pblock_update(l, p, h,
676             LIBNET_PBLOCK_IPV6_DESTOPTS_H));
677 bad:
678     libnet_pblock_delete(l, p);
679     return (-1);
680 }
681 
682 libnet_ptag_t
libnet_build_ipv6_hbhopts(uint8_t nh,uint8_t len,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)683 libnet_build_ipv6_hbhopts(uint8_t nh, uint8_t len, const uint8_t *payload,
684 uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
685 {
686     uint32_t n;
687     uint16_t h;
688     libnet_pblock_t *p;
689     struct libnet_ipv6_hbhopts_hdr ipv6_hbhopts_hdr;
690 
691     if (l == NULL)
692     {
693         return (-1);
694     }
695 
696     /* Important: IPv6 hop by hop opts information is specified using the
697      * payload interface!
698      */
699     n = LIBNET_IPV6_HBHOPTS_H + payload_s;
700     h = 0;
701 
702     if (LIBNET_IPV6_HBHOPTS_H + payload_s > IP_MAXPACKET)
703     {
704          snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,
705                  "%s(): IP packet too large\n", __func__);
706         return (-1);
707     }
708 
709     /*
710      *  Find the existing protocol block if a ptag is specified, or create
711      *  a new one.
712      */
713     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_IPV6_HBHOPTS_H);
714     if (p == NULL)
715     {
716         return (-1);
717     }
718 
719     memset(&ipv6_hbhopts_hdr, 0 , sizeof(ipv6_hbhopts_hdr));
720     ipv6_hbhopts_hdr.ip_nh  = nh;
721     ipv6_hbhopts_hdr.ip_len = len;
722 
723     /*
724      *  Appened the protocol unit to the list.
725      */
726     n = libnet_pblock_append(l, p, (uint8_t *)&ipv6_hbhopts_hdr,
727         LIBNET_IPV6_HBHOPTS_H);
728     if (n == -1)
729     {
730         goto bad;
731     }
732 
733     /* boilerplate payload sanity check / append macro */
734     LIBNET_DO_PAYLOAD(l, p);
735 
736     /*
737      *  Update the protocol block's meta information and return the protocol
738      *  tag id of this pblock.  This tag will be used to locate the pblock
739      *  in order to modify the protocol header in subsequent calls.
740      */
741     return (ptag ? ptag : libnet_pblock_update(l, p, h,
742             LIBNET_PBLOCK_IPV6_HBHOPTS_H));
743 bad:
744     libnet_pblock_delete(l, p);
745     return (-1);
746 }
747 
748 libnet_ptag_t
libnet_autobuild_ipv6(uint16_t len,uint8_t nh,struct libnet_in6_addr dst,libnet_t * l,libnet_ptag_t ptag)749 libnet_autobuild_ipv6(uint16_t len, uint8_t nh, struct libnet_in6_addr dst,
750             libnet_t *l, libnet_ptag_t ptag)
751 {
752     struct libnet_in6_addr src;
753 
754     src = libnet_get_ipaddr6(l);
755 
756     if (libnet_in6_is_error(src))
757     {
758         return (-1);
759     }
760 
761     return libnet_build_ipv6(0, 0, len, nh, 64, src, dst, NULL, 0, l, ptag);
762 }
763 
764