1 /*
2  *  $Id: libnet_build_icmp.c,v 1.17 2004/04/13 17:32:28 mike Exp $
3  *
4  *  libnet
5  *  libnet_build_icmp.c - ICMP packet assemblers
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 #include <assert.h>
43 
44 /* some common cruft for completing ICMP error packets */
45 #define LIBNET_BUILD_ICMP_ERR_FINISH(len)                                    \
46 do                                                                           \
47 {                                                                            \
48     n = libnet_pblock_append(l, p, (uint8_t *)&icmp_hdr, len);              \
49     if (n == -1)                                                             \
50     {                                                                        \
51         goto bad;                                                            \
52     }                                                                        \
53                                                                              \
54     if (payload_s && !payload)                                               \
55     {                                                                        \
56         snprintf(l->err_buf, LIBNET_ERRBUF_SIZE,                             \
57                 "%s(): payload inconsistency\n", __func__);                  \
58         goto bad;                                                            \
59     }                                                                        \
60                                                                              \
61     if (payload_s)                                                           \
62     {                                                                        \
63         n = libnet_pblock_append(l, p, payload, payload_s);                  \
64         if (n == -1)                                                         \
65         {                                                                    \
66             goto bad;                                                        \
67         }                                                                    \
68     }                                                                        \
69                                                                              \
70     if (sum == 0)                                                            \
71     {                                                                        \
72         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);                \
73     }                                                                        \
74 } while (0)
75 
76 libnet_ptag_t
libnet_build_icmpv4_echo(uint8_t type,uint8_t code,uint16_t sum,uint16_t id,uint16_t seq,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)77 libnet_build_icmpv4_echo(uint8_t type, uint8_t code, uint16_t sum,
78 uint16_t id, uint16_t seq, const uint8_t *payload, uint32_t payload_s,
79 libnet_t *l, libnet_ptag_t ptag)
80 {
81     uint32_t n, h;
82     libnet_pblock_t *p;
83     struct libnet_icmpv4_hdr icmp_hdr;
84 
85     if (l == NULL)
86     {
87         return (-1);
88     }
89 
90     n = LIBNET_ICMPV4_ECHO_H + payload_s;        /* size of memory block */
91     h = LIBNET_ICMPV4_ECHO_H + payload_s;        /* hl for checksum */
92 
93     /*
94      *  Find the existing protocol block if a ptag is specified, or create
95      *  a new one.
96      */
97     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_ECHO_H);
98     if (p == NULL)
99     {
100         return (-1);
101     }
102 
103     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
104     icmp_hdr.icmp_type = type;          /* packet type */
105     icmp_hdr.icmp_code = code;          /* packet code */
106     icmp_hdr.icmp_sum  = (sum ? htons(sum) : 0);  /* checksum */
107     icmp_hdr.icmp_id   = htons(id);            /* packet id */
108     icmp_hdr.icmp_seq  = htons(seq);           /* packet seq */
109 
110     n = libnet_pblock_append(l, p, (uint8_t *)&icmp_hdr, LIBNET_ICMPV4_ECHO_H);
111     if (n == -1)
112     {
113         goto bad;
114     }
115 
116     /* boilerplate payload sanity check / append macro */
117     LIBNET_DO_PAYLOAD(l, p);
118 
119     if (sum == 0)
120     {
121         /*
122          *  If checksum is zero, by default libnet will compute a checksum
123          *  for the user.  The programmer can override this by calling
124          *  libnet_toggle_checksum(l, ptag, 1);
125          */
126         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
127     }
128     return (ptag ? ptag : libnet_pblock_update(l, p, h,
129             LIBNET_PBLOCK_ICMPV4_ECHO_H));
130 bad:
131     libnet_pblock_delete(l, p);
132     return (-1);
133 }
134 
135 libnet_ptag_t
libnet_build_icmpv4_mask(uint8_t type,uint8_t code,uint16_t sum,uint16_t id,uint16_t seq,uint32_t mask,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)136 libnet_build_icmpv4_mask(uint8_t type, uint8_t code, uint16_t sum,
137 uint16_t id, uint16_t seq, uint32_t mask, const uint8_t *payload,
138 uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
139 {
140     uint32_t n, h;
141     libnet_pblock_t *p;
142     struct libnet_icmpv4_hdr icmp_hdr;
143 
144     if (l == NULL)
145     {
146         return (-1);
147     }
148 
149     n = LIBNET_ICMPV4_MASK_H + payload_s;        /* size of memory block */
150     h = LIBNET_ICMPV4_MASK_H + payload_s;        /* hl for checksum */
151 
152     /*
153      *  Find the existing protocol block if a ptag is specified, or create
154      *  a new one.
155      */
156     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_MASK_H);
157     if (p == NULL)
158     {
159         return (-1);
160     }
161 
162     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
163     icmp_hdr.icmp_type = type;          /* packet type */
164     icmp_hdr.icmp_code = code;          /* packet code */
165     icmp_hdr.icmp_sum  = (sum ? htons(sum) : 0);  /* checksum */
166     icmp_hdr.icmp_id   = htons(id);     /* packet id */
167     icmp_hdr.icmp_seq  = htons(seq);    /* packet seq */
168     icmp_hdr.icmp_mask = htonl(mask);   /* address mask */
169 
170     n = libnet_pblock_append(l, p, (uint8_t *)&icmp_hdr, LIBNET_ICMPV4_MASK_H);
171     if (n == -1)
172     {
173         goto bad;
174     }
175 
176     /* boilerplate payload sanity check / append macro */
177     LIBNET_DO_PAYLOAD(l, p);
178 
179     if (sum == 0)
180     {
181         /*
182          *  If checksum is zero, by default libnet will compute a checksum
183          *  for the user.  The programmer can override this by calling
184          *  libnet_toggle_checksum(l, ptag, 1);
185          */
186         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
187     }
188     return (ptag ? ptag : libnet_pblock_update(l, p, h,
189             LIBNET_PBLOCK_ICMPV4_MASK_H));
190 bad:
191     libnet_pblock_delete(l, p);
192     return (-1);
193 }
194 
195 libnet_ptag_t
libnet_build_icmpv4_timestamp(uint8_t type,uint8_t code,uint16_t sum,uint16_t id,uint16_t seq,uint32_t otime,uint32_t rtime,uint32_t ttime,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)196 libnet_build_icmpv4_timestamp(uint8_t type, uint8_t code, uint16_t sum,
197 uint16_t id, uint16_t seq, uint32_t otime, uint32_t rtime, uint32_t ttime,
198 const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
199 {
200     uint32_t n, h;
201     libnet_pblock_t *p;
202     struct libnet_icmpv4_hdr icmp_hdr;
203 
204     if (l == NULL)
205     {
206         return (-1);
207     }
208 
209     n = LIBNET_ICMPV4_TS_H + payload_s;        /* size of memory block */
210     h = LIBNET_ICMPV4_TS_H + payload_s;        /* hl for checksum */
211 
212     /*
213      *  Find the existing protocol block if a ptag is specified, or create
214      *  a new one.
215      */
216     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_TS_H);
217     if (p == NULL)
218     {
219         return (-1);
220     }
221 
222     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
223     icmp_hdr.icmp_type  = type;             /* packet type */
224     icmp_hdr.icmp_code  = code;             /* packet code */
225     icmp_hdr.icmp_sum   = (sum ? htons(sum) : 0); /* checksum */
226     icmp_hdr.icmp_id    = htons(id);        /* packet id */
227     icmp_hdr.icmp_seq   = htons(seq);       /* packet seq */
228     icmp_hdr.icmp_otime = htonl(otime);     /* original timestamp */
229     icmp_hdr.icmp_rtime = htonl(rtime);     /* receive timestamp */
230     icmp_hdr.icmp_ttime = htonl(ttime);     /* transmit timestamp */
231 
232     n = libnet_pblock_append(l, p, (uint8_t *)&icmp_hdr, LIBNET_ICMPV4_TS_H);
233     if (n == -1)
234     {
235         goto bad;
236     }
237 
238     /* boilerplate payload sanity check / append macro */
239     LIBNET_DO_PAYLOAD(l, p);
240 
241     if (sum == 0)
242     {
243         /*
244          *  If checksum is zero, by default libnet will compute a checksum
245          *  for the user.  The programmer can override this by calling
246          *  libnet_toggle_checksum(l, ptag, 1);
247          */
248         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
249     }
250     return (ptag ? ptag : libnet_pblock_update(l, p, h,
251             LIBNET_PBLOCK_ICMPV4_TS_H));
252 bad:
253     libnet_pblock_delete(l, p);
254     return (-1);
255 }
256 
257 libnet_ptag_t
libnet_build_icmpv4_unreach(uint8_t type,uint8_t code,uint16_t sum,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)258 libnet_build_icmpv4_unreach(uint8_t type, uint8_t code, uint16_t sum,
259 const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
260 {
261     uint32_t n, h;
262     libnet_pblock_t *p;
263     struct libnet_icmpv4_hdr icmp_hdr;
264 
265     if (l == NULL)
266     {
267         return (-1);
268     }
269     n = LIBNET_ICMPV4_UNREACH_H + payload_s;        /* size of memory block */
270 
271     /*
272      * FREDRAYNAL: as ICMP checksum includes what is embedded in
273      * the payload, and what is after the ICMP header, we need to include
274      * those 2 sizes.
275      */
276     h = LIBNET_ICMPV4_UNREACH_H + payload_s + l->total_size;
277 
278     /*
279      *  Find the existing protocol block if a ptag is specified, or create
280      *  a new one.
281      */
282     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_UNREACH_H);
283     if (p == NULL)
284     {
285         return (-1);
286     }
287 
288     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
289     icmp_hdr.icmp_type = type;          /* packet type */
290     icmp_hdr.icmp_code = code;          /* packet code */
291     icmp_hdr.icmp_sum  = (sum ? htons(sum) : 0);  /* checksum */
292     icmp_hdr.icmp_id   = 0;             /* must be 0 */
293     icmp_hdr.icmp_seq  = 0;             /* must be 0 */
294 
295     LIBNET_BUILD_ICMP_ERR_FINISH(LIBNET_ICMPV4_UNREACH_H);
296 
297     return (ptag ? ptag : libnet_pblock_update(l, p, h,
298             LIBNET_PBLOCK_ICMPV4_UNREACH_H));
299 bad:
300     libnet_pblock_delete(l, p);
301     return (-1);
302 }
303 
304 libnet_ptag_t
libnet_build_icmpv4_timeexceed(uint8_t type,uint8_t code,uint16_t sum,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)305 libnet_build_icmpv4_timeexceed(uint8_t type, uint8_t code, uint16_t sum,
306 const uint8_t *payload, uint32_t payload_s, libnet_t *l, libnet_ptag_t ptag)
307 {
308     uint32_t n, h;
309     libnet_pblock_t *p;
310     struct libnet_icmpv4_hdr icmp_hdr;
311 
312     if (l == NULL)
313     {
314         return (-1);
315     }
316 
317     /* size of memory block */
318     n = LIBNET_ICMPV4_TIMXCEED_H + payload_s;
319     /*
320      * FREDRAYNAL: as ICMP checksum includes what is embedded in
321      * the payload, and what is after the ICMP header, we need to include
322      * those 2 sizes.
323      */
324     h = LIBNET_ICMPV4_TIMXCEED_H + payload_s + l->total_size;
325 
326     /*
327      *  Find the existing protocol block if a ptag is specified, or create
328      *  a new one.
329      */
330     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_TIMXCEED_H);
331     if (p == NULL)
332     {
333         return (-1);
334     }
335 
336     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
337     icmp_hdr.icmp_type = type;          /* packet type */
338     icmp_hdr.icmp_code = code;          /* packet code */
339     icmp_hdr.icmp_sum  = (sum ? htons(sum) : 0);  /* checksum */
340     icmp_hdr.icmp_id   = 0;             /* must be 0 */
341     icmp_hdr.icmp_seq  = 0;             /* must be 0 */
342 
343     LIBNET_BUILD_ICMP_ERR_FINISH(LIBNET_ICMPV4_TIMXCEED_H);
344 
345     return (ptag ? ptag : libnet_pblock_update(l, p, h,
346             LIBNET_PBLOCK_ICMPV4_TIMXCEED_H));
347 bad:
348     libnet_pblock_delete(l, p);
349     return (-1);
350 }
351 
352 libnet_ptag_t
libnet_build_icmpv4_redirect(uint8_t type,uint8_t code,uint16_t sum,uint32_t gateway,const uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)353 libnet_build_icmpv4_redirect(uint8_t type, uint8_t code, uint16_t sum,
354 uint32_t gateway, const uint8_t *payload, uint32_t payload_s, libnet_t *l,
355 libnet_ptag_t ptag)
356 
357 {
358     uint32_t n, h;
359     libnet_pblock_t *p;
360     struct libnet_icmpv4_hdr icmp_hdr;
361 
362     if (l == NULL)
363     {
364         return (-1);
365     }
366 
367     n = LIBNET_ICMPV4_REDIRECT_H + payload_s;               /* size of memory block */
368     /*
369      * FREDRAYNAL: as ICMP checksum includes what is embedded in
370      * the payload, and what is after the ICMP header, we need to include
371      * those 2 sizes.
372      */
373     h = LIBNET_ICMPV4_REDIRECT_H + payload_s + l->total_size;
374 
375     /*
376      *  Find the existing protocol block if a ptag is specified, or create
377      *  a new one.
378      */
379     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV4_REDIRECT_H);
380     if (p == NULL)
381     {
382         return (-1);
383     }
384 
385     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
386     icmp_hdr.icmp_type      = type;             /* packet type */
387     icmp_hdr.icmp_code      = code;             /* packet code */
388     icmp_hdr.icmp_sum       = (sum ? htons(sum) : 0);  /* checksum */
389     icmp_hdr.hun.gateway    = gateway;
390 
391     LIBNET_BUILD_ICMP_ERR_FINISH(LIBNET_ICMPV4_REDIRECT_H);
392 
393     return (ptag ? ptag : libnet_pblock_update(l, p, h,
394             LIBNET_PBLOCK_ICMPV4_REDIRECT_H));
395 bad:
396     libnet_pblock_delete(l, p);
397     return (-1);
398 }
399 
400 
401 libnet_ptag_t
libnet_build_icmpv6_common(uint8_t type,uint8_t code,uint16_t sum,const void * specific,uint32_t specific_s,uint8_t pblock_type,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)402 libnet_build_icmpv6_common(
403         uint8_t type, uint8_t code, uint16_t sum,
404         const void* specific, uint32_t specific_s, uint8_t pblock_type,
405         uint8_t *payload, uint32_t payload_s,
406         libnet_t *l, libnet_ptag_t ptag
407         )
408 {
409     uint32_t n;
410     libnet_pblock_t *p;
411     struct libnet_icmpv6_hdr icmp_hdr;
412 
413     if (l == NULL)
414     {
415         return (-1);
416     }
417 
418     n = LIBNET_ICMPV6_COMMON_H + specific_s + payload_s;
419 
420     p = libnet_pblock_probe(l, ptag, n, pblock_type);
421 
422     if (p == NULL)
423     {
424         return (-1);
425     }
426 
427     memset(&icmp_hdr, 0, sizeof(icmp_hdr));
428     icmp_hdr.icmp_type = type;
429     icmp_hdr.icmp_code = code;
430     icmp_hdr.icmp_sum  = htons(sum);
431 
432     if (libnet_pblock_append(l, p, (uint8_t *)&icmp_hdr, LIBNET_ICMPV6_COMMON_H) < 0)
433     {
434         goto bad;
435     }
436 
437     if (libnet_pblock_append(l, p, specific, specific_s) < 0)
438     {
439         goto bad;
440     }
441 
442     if (libnet_pblock_append(l, p, payload, payload_s) < 0)
443     {
444         goto bad;
445     }
446 
447     if (sum == 0)
448     {
449         libnet_pblock_setflags(p, LIBNET_PBLOCK_DO_CHECKSUM);
450     }
451 
452     return ptag ? ptag : libnet_pblock_update(l, p, 0, pblock_type);
453 
454 bad:
455     libnet_pblock_delete(l, p);
456 
457     return -1;
458 }
459 
libnet_build_icmpv6(uint8_t type,uint8_t code,uint16_t sum,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)460 libnet_ptag_t libnet_build_icmpv6(uint8_t type, uint8_t code, uint16_t sum,
461                                   uint8_t* payload, uint32_t payload_s,
462                                   libnet_t* l, libnet_ptag_t ptag)
463 {
464     return libnet_build_icmpv6_common(
465             type, code, sum,
466             NULL, 0, LIBNET_PBLOCK_ICMPV6_UNREACH_H,
467             payload, payload_s,
468             l, ptag);
469 }
470 
471 libnet_ptag_t
libnet_build_icmpv6_unreach(uint8_t type,uint8_t code,uint16_t sum,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)472 libnet_build_icmpv6_unreach(
473         uint8_t type, uint8_t code, uint16_t sum,
474         uint8_t *payload, uint32_t payload_s,
475         libnet_t *l, libnet_ptag_t ptag
476         )
477 {
478     struct libnet_icmpv6_unreach specific;
479 
480     memset(&specific, 0, sizeof(specific));
481 
482     return libnet_build_icmpv6_common(
483             type, code, sum,
484             &specific, sizeof(specific), LIBNET_PBLOCK_ICMPV6_UNREACH_H,
485             payload, payload_s,
486             l, ptag);
487 }
488 
489 libnet_ptag_t
libnet_build_icmpv6_echo(uint8_t type,uint8_t code,uint16_t sum,uint16_t id,uint16_t seq,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)490 libnet_build_icmpv6_echo(
491         uint8_t type, uint8_t code, uint16_t sum,
492         uint16_t id, uint16_t seq,
493         uint8_t *payload, uint32_t payload_s,
494         libnet_t *l, libnet_ptag_t ptag
495         )
496 {
497     struct libnet_icmpv6_echo specific;
498 
499     memset(&specific, 0, sizeof(specific));
500     specific.id = htons(id);
501     specific.seq = htons(seq);
502 
503     return libnet_build_icmpv6_common(
504             type, code, sum,
505             &specific, sizeof(specific), LIBNET_PBLOCK_ICMPV6_ECHO_H,
506             payload, payload_s,
507             l, ptag);
508 }
509 
510 
libnet_build_icmpv6_ndp_nsol(uint8_t type,uint8_t code,uint16_t sum,struct libnet_in6_addr target,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)511 libnet_ptag_t libnet_build_icmpv6_ndp_nsol(
512         uint8_t type, uint8_t code, uint16_t sum,
513         struct libnet_in6_addr target,
514         uint8_t *payload, uint32_t payload_s,
515         libnet_t* l, libnet_ptag_t ptag)
516 {
517     struct libnet_icmpv6_ndp_nsol specific;
518 
519     memset(&specific, 0, sizeof(specific));
520     specific.reserved = 0;
521     specific.target_addr = target;
522 
523     return libnet_build_icmpv6_common(
524             type, code, sum,
525             &specific, sizeof(specific), LIBNET_PBLOCK_ICMPV6_NDP_NSOL_H,
526             payload, payload_s,
527             l, ptag);
528 }
529 
530 
libnet_build_icmpv6_ndp_nadv(uint8_t type,uint8_t code,uint16_t sum,uint32_t flags,struct libnet_in6_addr target,uint8_t * payload,uint32_t payload_s,libnet_t * l,libnet_ptag_t ptag)531 libnet_ptag_t libnet_build_icmpv6_ndp_nadv(
532         uint8_t type, uint8_t code, uint16_t sum,
533         uint32_t flags, struct libnet_in6_addr target,
534         uint8_t *payload, uint32_t payload_s,
535         libnet_t* l, libnet_ptag_t ptag)
536 {
537 
538     struct libnet_icmpv6_ndp_nadv specific;
539 
540     memset(&specific, 0, sizeof(specific));
541     specific.flags = htonl(flags);
542     specific.target_addr = target;
543 
544     return libnet_build_icmpv6_common(
545             type, code, sum,
546             &specific, sizeof(specific), LIBNET_PBLOCK_ICMPV6_NDP_NADV_H,
547             payload, payload_s,
548             l, ptag);
549 }
550 
libnet_build_icmpv6_ndp_opt(uint8_t type,uint8_t * option,uint32_t option_s,libnet_t * l,libnet_ptag_t ptag)551 libnet_ptag_t libnet_build_icmpv6_ndp_opt(
552         uint8_t type, uint8_t* option, uint32_t option_s,
553         libnet_t* l, libnet_ptag_t ptag)
554 {
555     struct libnet_icmpv6_ndp_opt opt;
556     uint32_t n;
557     static uint8_t pad[8] = { 0 };
558     uint32_t pad_s = 0;
559     libnet_pblock_t* p;
560 
561     if(l == NULL)
562         return -1;
563 
564     if(!option)
565         option_s = 0;
566 
567     /* options need to be padded to a multiple of 8-bytes, and opts.len is in units of 8-bytes */
568     n = sizeof(opt) + option_s;
569 
570     if(n % 8)
571     {
572         n += 8 - (n % 8);
573     }
574 
575     if(n > (0xff * 8))
576     {
577         return -1;
578     }
579 
580     pad_s = n - option_s - sizeof(opt);
581 
582     assert((n % 8) == 0);
583     assert(pad_s < sizeof(pad));
584 
585     p = libnet_pblock_probe(l, ptag, n, LIBNET_PBLOCK_ICMPV6_NDP_OPT_H);
586     if(p == NULL)
587         return -1;
588 
589     memset(&opt, 0, sizeof(opt));
590     opt.type = type;
591     opt.len  = n / 8;
592 
593     if(libnet_pblock_append(l, p, &opt, sizeof(opt)) == -1)
594         goto bad;
595 
596     if(libnet_pblock_append(l, p, option, option_s) == -1)
597         goto bad;
598 
599     if(libnet_pblock_append(l, p, pad, pad_s) == -1)
600         goto bad;
601 
602     return ptag ? ptag : libnet_pblock_update(l, p, n, LIBNET_PBLOCK_ICMPV6_NDP_OPT_H);
603 
604 bad:
605     libnet_pblock_delete(l, p);
606     return -1;
607 }
608 
609