1 /*
2 * Redistribution and use in source and binary forms, with or without
3 * modification, are permitted provided that: (1) source code
4 * distributions retain the above copyright notice and this paragraph
5 * in its entirety, and (2) distributions including binary code include
6 * the above copyright notice and this paragraph in its entirety in
7 * the documentation or other materials provided with the distribution.
8 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9 * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
10 * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11 * FOR A PARTICULAR PURPOSE.
12 *
13 * Original code by Andy Heffernan (ahh@juniper.net)
14 */
15
16 #include <sys/cdefs.h>
17 #ifndef lint
18 __RCSID("$NetBSD: print-pgm.c,v 1.12 2024/09/02 16:15:32 christos Exp $");
19 #endif
20
21 /* \summary: Pragmatic General Multicast (PGM) printer */
22
23 #include <config.h>
24
25 #include "netdissect-stdinc.h"
26
27 #include "netdissect.h"
28 #include "extract.h"
29 #include "addrtoname.h"
30 #include "addrtostr.h"
31
32 #include "ip.h"
33 #include "ip6.h"
34 #include "ipproto.h"
35 #include "af.h"
36
37 /*
38 * PGM header (RFC 3208)
39 */
40 struct pgm_header {
41 nd_uint16_t pgm_sport;
42 nd_uint16_t pgm_dport;
43 nd_uint8_t pgm_type;
44 nd_uint8_t pgm_options;
45 nd_uint16_t pgm_sum;
46 nd_byte pgm_gsid[6];
47 nd_uint16_t pgm_length;
48 };
49
50 struct pgm_spm {
51 nd_uint32_t pgms_seq;
52 nd_uint32_t pgms_trailseq;
53 nd_uint32_t pgms_leadseq;
54 nd_uint16_t pgms_nla_afi;
55 nd_uint16_t pgms_reserved;
56 /* ... uint8_t pgms_nla[0]; */
57 /* ... options */
58 };
59
60 struct pgm_nak {
61 nd_uint32_t pgmn_seq;
62 nd_uint16_t pgmn_source_afi;
63 nd_uint16_t pgmn_reserved;
64 /* ... uint8_t pgmn_source[0]; */
65 /* ... uint16_t pgmn_group_afi */
66 /* ... uint16_t pgmn_reserved2; */
67 /* ... uint8_t pgmn_group[0]; */
68 /* ... options */
69 };
70
71 struct pgm_ack {
72 nd_uint32_t pgma_rx_max_seq;
73 nd_uint32_t pgma_bitmap;
74 /* ... options */
75 };
76
77 struct pgm_poll {
78 nd_uint32_t pgmp_seq;
79 nd_uint16_t pgmp_round;
80 nd_uint16_t pgmp_subtype;
81 nd_uint16_t pgmp_nla_afi;
82 nd_uint16_t pgmp_reserved;
83 /* ... uint8_t pgmp_nla[0]; */
84 /* ... options */
85 };
86
87 struct pgm_polr {
88 nd_uint32_t pgmp_seq;
89 nd_uint16_t pgmp_round;
90 nd_uint16_t pgmp_reserved;
91 /* ... options */
92 };
93
94 struct pgm_data {
95 nd_uint32_t pgmd_seq;
96 nd_uint32_t pgmd_trailseq;
97 /* ... options */
98 };
99
100 typedef enum _pgm_type {
101 PGM_SPM = 0, /* source path message */
102 PGM_POLL = 1, /* POLL Request */
103 PGM_POLR = 2, /* POLL Response */
104 PGM_ODATA = 4, /* original data */
105 PGM_RDATA = 5, /* repair data */
106 PGM_NAK = 8, /* NAK */
107 PGM_NULLNAK = 9, /* Null NAK */
108 PGM_NCF = 10, /* NAK Confirmation */
109 PGM_ACK = 11, /* ACK for congestion control */
110 PGM_SPMR = 12, /* SPM request */
111 PGM_MAX = 255
112 } pgm_type;
113
114 #define PGM_OPT_BIT_PRESENT 0x01
115 #define PGM_OPT_BIT_NETWORK 0x02
116 #define PGM_OPT_BIT_VAR_PKTLEN 0x40
117 #define PGM_OPT_BIT_PARITY 0x80
118
119 #define PGM_OPT_LENGTH 0x00
120 #define PGM_OPT_FRAGMENT 0x01
121 #define PGM_OPT_NAK_LIST 0x02
122 #define PGM_OPT_JOIN 0x03
123 #define PGM_OPT_NAK_BO_IVL 0x04
124 #define PGM_OPT_NAK_BO_RNG 0x05
125
126 #define PGM_OPT_REDIRECT 0x07
127 #define PGM_OPT_PARITY_PRM 0x08
128 #define PGM_OPT_PARITY_GRP 0x09
129 #define PGM_OPT_CURR_TGSIZE 0x0A
130 #define PGM_OPT_NBR_UNREACH 0x0B
131 #define PGM_OPT_PATH_NLA 0x0C
132
133 #define PGM_OPT_SYN 0x0D
134 #define PGM_OPT_FIN 0x0E
135 #define PGM_OPT_RST 0x0F
136 #define PGM_OPT_CR 0x10
137 #define PGM_OPT_CRQST 0x11
138
139 #define PGM_OPT_PGMCC_DATA 0x12
140 #define PGM_OPT_PGMCC_FEEDBACK 0x13
141
142 #define PGM_OPT_MASK 0x7f
143
144 #define PGM_OPT_END 0x80 /* end of options marker */
145
146 #define PGM_MIN_OPT_LEN 4
147
148 UNALIGNED_OK
149 void
pgm_print(netdissect_options * ndo,const u_char * bp,u_int length,const u_char * bp2)150 pgm_print(netdissect_options *ndo,
151 const u_char *bp, u_int length,
152 const u_char *bp2)
153 {
154 const struct pgm_header *pgm;
155 const struct ip *ip;
156 uint8_t pgm_type_val;
157 uint16_t sport, dport;
158 u_int nla_afnum;
159 char nla_buf[INET6_ADDRSTRLEN];
160 const struct ip6_hdr *ip6;
161 uint8_t opt_type, opt_len;
162 uint32_t seq, opts_len, len, offset;
163
164 ndo->ndo_protocol = "pgm";
165 pgm = (const struct pgm_header *)bp;
166 ip = (const struct ip *)bp2;
167 if (IP_V(ip) == 6)
168 ip6 = (const struct ip6_hdr *)bp2;
169 else
170 ip6 = NULL;
171 if (!ND_TTEST_2(pgm->pgm_dport)) {
172 if (ip6) {
173 ND_PRINT("%s > %s:",
174 GET_IP6ADDR_STRING(ip6->ip6_src),
175 GET_IP6ADDR_STRING(ip6->ip6_dst));
176 } else {
177 ND_PRINT("%s > %s:",
178 GET_IPADDR_STRING(ip->ip_src),
179 GET_IPADDR_STRING(ip->ip_dst));
180 }
181 nd_print_trunc(ndo);
182 return;
183 }
184
185 sport = GET_BE_U_2(pgm->pgm_sport);
186 dport = GET_BE_U_2(pgm->pgm_dport);
187
188 if (ip6) {
189 if (GET_U_1(ip6->ip6_nxt) == IPPROTO_PGM) {
190 ND_PRINT("%s.%s > %s.%s: ",
191 GET_IP6ADDR_STRING(ip6->ip6_src),
192 tcpport_string(ndo, sport),
193 GET_IP6ADDR_STRING(ip6->ip6_dst),
194 tcpport_string(ndo, dport));
195 } else {
196 ND_PRINT("%s > %s: ",
197 tcpport_string(ndo, sport), tcpport_string(ndo, dport));
198 }
199 } else {
200 if (GET_U_1(ip->ip_p) == IPPROTO_PGM) {
201 ND_PRINT("%s.%s > %s.%s: ",
202 GET_IPADDR_STRING(ip->ip_src),
203 tcpport_string(ndo, sport),
204 GET_IPADDR_STRING(ip->ip_dst),
205 tcpport_string(ndo, dport));
206 } else {
207 ND_PRINT("%s > %s: ",
208 tcpport_string(ndo, sport), tcpport_string(ndo, dport));
209 }
210 }
211
212 ND_TCHECK_SIZE(pgm);
213
214 ND_PRINT("PGM, length %u", GET_BE_U_2(pgm->pgm_length));
215
216 if (!ndo->ndo_vflag)
217 return;
218
219 pgm_type_val = GET_U_1(pgm->pgm_type);
220 ND_PRINT(" 0x%02x%02x%02x%02x%02x%02x ",
221 pgm->pgm_gsid[0],
222 pgm->pgm_gsid[1],
223 pgm->pgm_gsid[2],
224 pgm->pgm_gsid[3],
225 pgm->pgm_gsid[4],
226 pgm->pgm_gsid[5]);
227 switch (pgm_type_val) {
228 case PGM_SPM: {
229 const struct pgm_spm *spm;
230
231 spm = (const struct pgm_spm *)(pgm + 1);
232 ND_TCHECK_SIZE(spm);
233 bp = (const u_char *) (spm + 1);
234
235 switch (GET_BE_U_2(spm->pgms_nla_afi)) {
236 case AFNUM_INET:
237 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
238 addrtostr(bp, nla_buf, sizeof(nla_buf));
239 bp += sizeof(nd_ipv4);
240 break;
241 case AFNUM_INET6:
242 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
243 addrtostr6(bp, nla_buf, sizeof(nla_buf));
244 bp += sizeof(nd_ipv6);
245 break;
246 default:
247 goto trunc;
248 break;
249 }
250
251 ND_PRINT("SPM seq %u trail %u lead %u nla %s",
252 GET_BE_U_4(spm->pgms_seq),
253 GET_BE_U_4(spm->pgms_trailseq),
254 GET_BE_U_4(spm->pgms_leadseq),
255 nla_buf);
256 break;
257 }
258
259 case PGM_POLL: {
260 const struct pgm_poll *pgm_poll;
261 uint32_t ivl, rnd, mask;
262
263 pgm_poll = (const struct pgm_poll *)(pgm + 1);
264 ND_TCHECK_SIZE(pgm_poll);
265 bp = (const u_char *) (pgm_poll + 1);
266
267 switch (GET_BE_U_2(pgm_poll->pgmp_nla_afi)) {
268 case AFNUM_INET:
269 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
270 addrtostr(bp, nla_buf, sizeof(nla_buf));
271 bp += sizeof(nd_ipv4);
272 break;
273 case AFNUM_INET6:
274 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
275 addrtostr6(bp, nla_buf, sizeof(nla_buf));
276 bp += sizeof(nd_ipv6);
277 break;
278 default:
279 goto trunc;
280 break;
281 }
282
283 ivl = GET_BE_U_4(bp);
284 bp += sizeof(uint32_t);
285
286 rnd = GET_BE_U_4(bp);
287 bp += sizeof(uint32_t);
288
289 mask = GET_BE_U_4(bp);
290 bp += sizeof(uint32_t);
291
292 ND_PRINT("POLL seq %u round %u nla %s ivl %u rnd 0x%08x "
293 "mask 0x%08x", GET_BE_U_4(pgm_poll->pgmp_seq),
294 GET_BE_U_2(pgm_poll->pgmp_round), nla_buf, ivl, rnd,
295 mask);
296 break;
297 }
298 case PGM_POLR: {
299 const struct pgm_polr *polr_msg;
300
301 polr_msg = (const struct pgm_polr *)(pgm + 1);
302 ND_TCHECK_SIZE(polr_msg);
303 ND_PRINT("POLR seq %u round %u",
304 GET_BE_U_4(polr_msg->pgmp_seq),
305 GET_BE_U_2(polr_msg->pgmp_round));
306 bp = (const u_char *) (polr_msg + 1);
307 break;
308 }
309 case PGM_ODATA: {
310 const struct pgm_data *odata;
311
312 odata = (const struct pgm_data *)(pgm + 1);
313 ND_TCHECK_SIZE(odata);
314 ND_PRINT("ODATA trail %u seq %u",
315 GET_BE_U_4(odata->pgmd_trailseq),
316 GET_BE_U_4(odata->pgmd_seq));
317 bp = (const u_char *) (odata + 1);
318 break;
319 }
320
321 case PGM_RDATA: {
322 const struct pgm_data *rdata;
323
324 rdata = (const struct pgm_data *)(pgm + 1);
325 ND_TCHECK_SIZE(rdata);
326 ND_PRINT("RDATA trail %u seq %u",
327 GET_BE_U_4(rdata->pgmd_trailseq),
328 GET_BE_U_4(rdata->pgmd_seq));
329 bp = (const u_char *) (rdata + 1);
330 break;
331 }
332
333 case PGM_NAK:
334 case PGM_NULLNAK:
335 case PGM_NCF: {
336 const struct pgm_nak *nak;
337 char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
338
339 nak = (const struct pgm_nak *)(pgm + 1);
340 ND_TCHECK_SIZE(nak);
341 bp = (const u_char *) (nak + 1);
342
343 /*
344 * Skip past the source, saving info along the way
345 * and stopping if we don't have enough.
346 */
347 switch (GET_BE_U_2(nak->pgmn_source_afi)) {
348 case AFNUM_INET:
349 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
350 addrtostr(bp, source_buf, sizeof(source_buf));
351 bp += sizeof(nd_ipv4);
352 break;
353 case AFNUM_INET6:
354 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
355 addrtostr6(bp, source_buf, sizeof(source_buf));
356 bp += sizeof(nd_ipv6);
357 break;
358 default:
359 goto trunc;
360 break;
361 }
362
363 /*
364 * Skip past the group, saving info along the way
365 * and stopping if we don't have enough.
366 */
367 bp += (2 * sizeof(uint16_t));
368 switch (GET_BE_U_2(bp)) {
369 case AFNUM_INET:
370 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
371 addrtostr(bp, group_buf, sizeof(group_buf));
372 bp += sizeof(nd_ipv4);
373 break;
374 case AFNUM_INET6:
375 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
376 addrtostr6(bp, group_buf, sizeof(group_buf));
377 bp += sizeof(nd_ipv6);
378 break;
379 default:
380 goto trunc;
381 break;
382 }
383
384 /*
385 * Options decoding can go here.
386 */
387 switch (pgm_type_val) {
388 case PGM_NAK:
389 ND_PRINT("NAK ");
390 break;
391 case PGM_NULLNAK:
392 ND_PRINT("NNAK ");
393 break;
394 case PGM_NCF:
395 ND_PRINT("NCF ");
396 break;
397 default:
398 break;
399 }
400 ND_PRINT("(%s -> %s), seq %u",
401 source_buf, group_buf, GET_BE_U_4(nak->pgmn_seq));
402 break;
403 }
404
405 case PGM_ACK: {
406 const struct pgm_ack *ack;
407
408 ack = (const struct pgm_ack *)(pgm + 1);
409 ND_TCHECK_SIZE(ack);
410 ND_PRINT("ACK seq %u",
411 GET_BE_U_4(ack->pgma_rx_max_seq));
412 bp = (const u_char *) (ack + 1);
413 break;
414 }
415
416 case PGM_SPMR:
417 ND_PRINT("SPMR");
418 break;
419
420 default:
421 ND_PRINT("UNKNOWN type 0x%02x", pgm_type_val);
422 break;
423
424 }
425 if (GET_U_1(pgm->pgm_options) & PGM_OPT_BIT_PRESENT) {
426
427 /*
428 * make sure there's enough for the first option header
429 */
430 ND_TCHECK_LEN(bp, PGM_MIN_OPT_LEN);
431
432 /*
433 * That option header MUST be an OPT_LENGTH option
434 * (see the first paragraph of section 9.1 in RFC 3208).
435 */
436 opt_type = GET_U_1(bp);
437 bp++;
438 if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
439 ND_PRINT("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
440 return;
441 }
442 opt_len = GET_U_1(bp);
443 bp++;
444 if (opt_len != 4) {
445 ND_PRINT("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
446 return;
447 }
448 opts_len = GET_BE_U_2(bp);
449 bp += sizeof(uint16_t);
450 if (opts_len < 4) {
451 ND_PRINT("[Bad total option length %u < 4]", opts_len);
452 return;
453 }
454 ND_PRINT(" OPTS LEN %u", opts_len);
455 opts_len -= 4;
456
457 while (opts_len) {
458 if (opts_len < PGM_MIN_OPT_LEN) {
459 ND_PRINT("[Total option length leaves no room for final option]");
460 return;
461 }
462 opt_type = GET_U_1(bp);
463 bp++;
464 opt_len = GET_U_1(bp);
465 bp++;
466 if (opt_len < PGM_MIN_OPT_LEN) {
467 ND_PRINT("[Bad option, length %u < %u]", opt_len,
468 PGM_MIN_OPT_LEN);
469 break;
470 }
471 if (opts_len < opt_len) {
472 ND_PRINT("[Total option length leaves no room for final option]");
473 return;
474 }
475 ND_TCHECK_LEN(bp, opt_len - 2);
476
477 switch (opt_type & PGM_OPT_MASK) {
478 case PGM_OPT_LENGTH:
479 #define PGM_OPT_LENGTH_LEN (2+2)
480 if (opt_len != PGM_OPT_LENGTH_LEN) {
481 ND_PRINT("[Bad OPT_LENGTH option, length %u != %u]",
482 opt_len, PGM_OPT_LENGTH_LEN);
483 return;
484 }
485 ND_PRINT(" OPTS LEN (extra?) %u", GET_BE_U_2(bp));
486 bp += 2;
487 opts_len -= PGM_OPT_LENGTH_LEN;
488 break;
489
490 case PGM_OPT_FRAGMENT:
491 #define PGM_OPT_FRAGMENT_LEN (2+2+4+4+4)
492 if (opt_len != PGM_OPT_FRAGMENT_LEN) {
493 ND_PRINT("[Bad OPT_FRAGMENT option, length %u != %u]",
494 opt_len, PGM_OPT_FRAGMENT_LEN);
495 return;
496 }
497 bp += 2;
498 seq = GET_BE_U_4(bp);
499 bp += 4;
500 offset = GET_BE_U_4(bp);
501 bp += 4;
502 len = GET_BE_U_4(bp);
503 bp += 4;
504 ND_PRINT(" FRAG seq %u off %u len %u", seq, offset, len);
505 opts_len -= PGM_OPT_FRAGMENT_LEN;
506 break;
507
508 case PGM_OPT_NAK_LIST:
509 bp += 2;
510 opt_len -= 4; /* option header */
511 ND_PRINT(" NAK LIST");
512 while (opt_len) {
513 if (opt_len < 4) {
514 ND_PRINT("[Option length not a multiple of 4]");
515 return;
516 }
517 ND_PRINT(" %u", GET_BE_U_4(bp));
518 bp += 4;
519 opt_len -= 4;
520 opts_len -= 4;
521 }
522 break;
523
524 case PGM_OPT_JOIN:
525 #define PGM_OPT_JOIN_LEN (2+2+4)
526 if (opt_len != PGM_OPT_JOIN_LEN) {
527 ND_PRINT("[Bad OPT_JOIN option, length %u != %u]",
528 opt_len, PGM_OPT_JOIN_LEN);
529 return;
530 }
531 bp += 2;
532 seq = GET_BE_U_4(bp);
533 bp += 4;
534 ND_PRINT(" JOIN %u", seq);
535 opts_len -= PGM_OPT_JOIN_LEN;
536 break;
537
538 case PGM_OPT_NAK_BO_IVL:
539 #define PGM_OPT_NAK_BO_IVL_LEN (2+2+4+4)
540 if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
541 ND_PRINT("[Bad OPT_NAK_BO_IVL option, length %u != %u]",
542 opt_len, PGM_OPT_NAK_BO_IVL_LEN);
543 return;
544 }
545 bp += 2;
546 offset = GET_BE_U_4(bp);
547 bp += 4;
548 seq = GET_BE_U_4(bp);
549 bp += 4;
550 ND_PRINT(" BACKOFF ivl %u ivlseq %u", offset, seq);
551 opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
552 break;
553
554 case PGM_OPT_NAK_BO_RNG:
555 #define PGM_OPT_NAK_BO_RNG_LEN (2+2+4+4)
556 if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
557 ND_PRINT("[Bad OPT_NAK_BO_RNG option, length %u != %u]",
558 opt_len, PGM_OPT_NAK_BO_RNG_LEN);
559 return;
560 }
561 bp += 2;
562 offset = GET_BE_U_4(bp);
563 bp += 4;
564 seq = GET_BE_U_4(bp);
565 bp += 4;
566 ND_PRINT(" BACKOFF max %u min %u", offset, seq);
567 opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
568 break;
569
570 case PGM_OPT_REDIRECT:
571 #define PGM_OPT_REDIRECT_FIXED_LEN (2+2+2+2)
572 if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
573 ND_PRINT("[Bad OPT_REDIRECT option, length %u < %u]",
574 opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
575 return;
576 }
577 bp += 2;
578 nla_afnum = GET_BE_U_2(bp);
579 bp += 2+2;
580 switch (nla_afnum) {
581 case AFNUM_INET:
582 if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4)) {
583 ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
584 opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
585 return;
586 }
587 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
588 addrtostr(bp, nla_buf, sizeof(nla_buf));
589 bp += sizeof(nd_ipv4);
590 opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4);
591 break;
592 case AFNUM_INET6:
593 if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6)) {
594 ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
595 opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
596 return;
597 }
598 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
599 addrtostr6(bp, nla_buf, sizeof(nla_buf));
600 bp += sizeof(nd_ipv6);
601 opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6);
602 break;
603 default:
604 goto trunc;
605 break;
606 }
607
608 ND_PRINT(" REDIRECT %s", nla_buf);
609 break;
610
611 case PGM_OPT_PARITY_PRM:
612 #define PGM_OPT_PARITY_PRM_LEN (2+2+4)
613 if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
614 ND_PRINT("[Bad OPT_PARITY_PRM option, length %u != %u]",
615 opt_len, PGM_OPT_PARITY_PRM_LEN);
616 return;
617 }
618 bp += 2;
619 len = GET_BE_U_4(bp);
620 bp += 4;
621 ND_PRINT(" PARITY MAXTGS %u", len);
622 opts_len -= PGM_OPT_PARITY_PRM_LEN;
623 break;
624
625 case PGM_OPT_PARITY_GRP:
626 #define PGM_OPT_PARITY_GRP_LEN (2+2+4)
627 if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
628 ND_PRINT("[Bad OPT_PARITY_GRP option, length %u != %u]",
629 opt_len, PGM_OPT_PARITY_GRP_LEN);
630 return;
631 }
632 bp += 2;
633 seq = GET_BE_U_4(bp);
634 bp += 4;
635 ND_PRINT(" PARITY GROUP %u", seq);
636 opts_len -= PGM_OPT_PARITY_GRP_LEN;
637 break;
638
639 case PGM_OPT_CURR_TGSIZE:
640 #define PGM_OPT_CURR_TGSIZE_LEN (2+2+4)
641 if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
642 ND_PRINT("[Bad OPT_CURR_TGSIZE option, length %u != %u]",
643 opt_len, PGM_OPT_CURR_TGSIZE_LEN);
644 return;
645 }
646 bp += 2;
647 len = GET_BE_U_4(bp);
648 bp += 4;
649 ND_PRINT(" PARITY ATGS %u", len);
650 opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
651 break;
652
653 case PGM_OPT_NBR_UNREACH:
654 #define PGM_OPT_NBR_UNREACH_LEN (2+2)
655 if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
656 ND_PRINT("[Bad OPT_NBR_UNREACH option, length %u != %u]",
657 opt_len, PGM_OPT_NBR_UNREACH_LEN);
658 return;
659 }
660 bp += 2;
661 ND_PRINT(" NBR_UNREACH");
662 opts_len -= PGM_OPT_NBR_UNREACH_LEN;
663 break;
664
665 case PGM_OPT_PATH_NLA:
666 ND_PRINT(" PATH_NLA [%u]", opt_len);
667 bp += opt_len;
668 opts_len -= opt_len;
669 break;
670
671 case PGM_OPT_SYN:
672 #define PGM_OPT_SYN_LEN (2+2)
673 if (opt_len != PGM_OPT_SYN_LEN) {
674 ND_PRINT("[Bad OPT_SYN option, length %u != %u]",
675 opt_len, PGM_OPT_SYN_LEN);
676 return;
677 }
678 bp += 2;
679 ND_PRINT(" SYN");
680 opts_len -= PGM_OPT_SYN_LEN;
681 break;
682
683 case PGM_OPT_FIN:
684 #define PGM_OPT_FIN_LEN (2+2)
685 if (opt_len != PGM_OPT_FIN_LEN) {
686 ND_PRINT("[Bad OPT_FIN option, length %u != %u]",
687 opt_len, PGM_OPT_FIN_LEN);
688 return;
689 }
690 bp += 2;
691 ND_PRINT(" FIN");
692 opts_len -= PGM_OPT_FIN_LEN;
693 break;
694
695 case PGM_OPT_RST:
696 #define PGM_OPT_RST_LEN (2+2)
697 if (opt_len != PGM_OPT_RST_LEN) {
698 ND_PRINT("[Bad OPT_RST option, length %u != %u]",
699 opt_len, PGM_OPT_RST_LEN);
700 return;
701 }
702 bp += 2;
703 ND_PRINT(" RST");
704 opts_len -= PGM_OPT_RST_LEN;
705 break;
706
707 case PGM_OPT_CR:
708 ND_PRINT(" CR");
709 bp += opt_len;
710 opts_len -= opt_len;
711 break;
712
713 case PGM_OPT_CRQST:
714 #define PGM_OPT_CRQST_LEN (2+2)
715 if (opt_len != PGM_OPT_CRQST_LEN) {
716 ND_PRINT("[Bad OPT_CRQST option, length %u != %u]",
717 opt_len, PGM_OPT_CRQST_LEN);
718 return;
719 }
720 bp += 2;
721 ND_PRINT(" CRQST");
722 opts_len -= PGM_OPT_CRQST_LEN;
723 break;
724
725 case PGM_OPT_PGMCC_DATA:
726 #define PGM_OPT_PGMCC_DATA_FIXED_LEN (2+2+4+2+2)
727 if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
728 ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u < %u]",
729 opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
730 return;
731 }
732 bp += 2;
733 offset = GET_BE_U_4(bp);
734 bp += 4;
735 nla_afnum = GET_BE_U_2(bp);
736 bp += 2+2;
737 switch (nla_afnum) {
738 case AFNUM_INET:
739 if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4)) {
740 ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
741 opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
742 return;
743 }
744 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
745 addrtostr(bp, nla_buf, sizeof(nla_buf));
746 bp += sizeof(nd_ipv4);
747 opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4);
748 break;
749 case AFNUM_INET6:
750 if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6)) {
751 ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
752 opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
753 return;
754 }
755 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
756 addrtostr6(bp, nla_buf, sizeof(nla_buf));
757 bp += sizeof(nd_ipv6);
758 opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6);
759 break;
760 default:
761 goto trunc;
762 break;
763 }
764
765 ND_PRINT(" PGMCC DATA %u %s", offset, nla_buf);
766 break;
767
768 case PGM_OPT_PGMCC_FEEDBACK:
769 #define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN (2+2+4+2+2)
770 if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
771 ND_PRINT("[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
772 opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
773 return;
774 }
775 bp += 2;
776 offset = GET_BE_U_4(bp);
777 bp += 4;
778 nla_afnum = GET_BE_U_2(bp);
779 bp += 2+2;
780 switch (nla_afnum) {
781 case AFNUM_INET:
782 if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4)) {
783 ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
784 opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
785 return;
786 }
787 ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
788 addrtostr(bp, nla_buf, sizeof(nla_buf));
789 bp += sizeof(nd_ipv4);
790 opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4);
791 break;
792 case AFNUM_INET6:
793 if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6)) {
794 ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
795 opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
796 return;
797 }
798 ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
799 addrtostr6(bp, nla_buf, sizeof(nla_buf));
800 bp += sizeof(nd_ipv6);
801 opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6);
802 break;
803 default:
804 goto trunc;
805 break;
806 }
807
808 ND_PRINT(" PGMCC FEEDBACK %u %s", offset, nla_buf);
809 break;
810
811 default:
812 ND_PRINT(" OPT_%02X [%u] ", opt_type, opt_len);
813 bp += opt_len;
814 opts_len -= opt_len;
815 break;
816 }
817
818 if (opt_type & PGM_OPT_END)
819 break;
820 }
821 }
822
823 ND_PRINT(" [%u]", length);
824 if (ndo->ndo_packettype == PT_PGM_ZMTP1 &&
825 (pgm_type_val == PGM_ODATA || pgm_type_val == PGM_RDATA))
826 zmtp1_datagram_print(ndo, bp,
827 GET_BE_U_2(pgm->pgm_length));
828
829 return;
830
831 trunc:
832 nd_print_trunc(ndo);
833 }
834