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