1147899Ssam/*
2147899Ssam * Redistribution and use in source and binary forms, with or without
3147899Ssam * modification, are permitted provided that: (1) source code
4147899Ssam * distributions retain the above copyright notice and this paragraph
5147899Ssam * in its entirety, and (2) distributions including binary code include
6147899Ssam * the above copyright notice and this paragraph in its entirety in
7147899Ssam * the documentation or other materials provided with the distribution.
8147899Ssam * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
9147899Ssam * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
10147899Ssam * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
11147899Ssam * FOR A PARTICULAR PURPOSE.
12147899Ssam *
13147899Ssam * Original code by Andy Heffernan (ahh@juniper.net)
14147899Ssam */
15147899Ssam
16147899Ssam#ifndef lint
17147899Ssamstatic const char rcsid[] _U_ =
18190207Srpaulo    "@(#) $Header: /tcpdump/master/tcpdump/print-pgm.c,v 1.5 2005-06-07 22:05:58 guy Exp $";
19147899Ssam#endif
20147899Ssam
21147899Ssam#ifdef HAVE_CONFIG_H
22147899Ssam#include "config.h"
23147899Ssam#endif
24147899Ssam
25147899Ssam#include <tcpdump-stdinc.h>
26147899Ssam
27147899Ssam#include <stdio.h>
28147899Ssam#include <stdlib.h>
29147899Ssam#include <string.h>
30147899Ssam
31147899Ssam#include "interface.h"
32147899Ssam#include "extract.h"
33147899Ssam#include "addrtoname.h"
34147899Ssam
35147899Ssam#include "ip.h"
36147899Ssam#ifdef INET6
37147899Ssam#include "ip6.h"
38147899Ssam#endif
39147899Ssam#include "ipproto.h"
40147899Ssam
41147899Ssam/*
42147899Ssam * PGM header (RFC 3208)
43147899Ssam */
44147899Ssamstruct pgm_header {
45147899Ssam    u_int16_t	pgm_sport;
46147899Ssam    u_int16_t	pgm_dport;
47147899Ssam    u_int8_t	pgm_type;
48147899Ssam    u_int8_t	pgm_options;
49147899Ssam    u_int16_t	pgm_sum;
50147899Ssam    u_int8_t	pgm_gsid[6];
51147899Ssam    u_int16_t	pgm_length;
52147899Ssam};
53147899Ssam
54147899Ssamstruct pgm_spm {
55147899Ssam    u_int32_t	pgms_seq;
56147899Ssam    u_int32_t	pgms_trailseq;
57147899Ssam    u_int32_t	pgms_leadseq;
58147899Ssam    u_int16_t	pgms_nla_afi;
59147899Ssam    u_int16_t	pgms_reserved;
60147899Ssam    /* ... u_int8_t	pgms_nla[0]; */
61147899Ssam    /* ... options */
62147899Ssam};
63147899Ssam
64147899Ssamstruct pgm_nak {
65147899Ssam    u_int32_t	pgmn_seq;
66147899Ssam    u_int16_t	pgmn_source_afi;
67147899Ssam    u_int16_t	pgmn_reserved;
68147899Ssam    /* ... u_int8_t	pgmn_source[0]; */
69147899Ssam    /* ... u_int16_t	pgmn_group_afi */
70147899Ssam    /* ... u_int16_t	pgmn_reserved2; */
71147899Ssam    /* ... u_int8_t	pgmn_group[0]; */
72147899Ssam    /* ... options */
73147899Ssam};
74147899Ssam
75236192Sdelphijstruct pgm_ack {
76236192Sdelphij    u_int32_t	pgma_rx_max_seq;
77236192Sdelphij    u_int32_t	pgma_bitmap;
78236192Sdelphij    /* ... options */
79236192Sdelphij};
80236192Sdelphij
81147899Ssamstruct pgm_poll {
82147899Ssam    u_int32_t	pgmp_seq;
83147899Ssam    u_int16_t	pgmp_round;
84147899Ssam    u_int16_t	pgmp_reserved;
85147899Ssam    /* ... options */
86147899Ssam};
87147899Ssam
88147899Ssamstruct pgm_polr {
89147899Ssam    u_int32_t	pgmp_seq;
90147899Ssam    u_int16_t	pgmp_round;
91147899Ssam    u_int16_t	pgmp_subtype;
92147899Ssam    u_int16_t	pgmp_nla_afi;
93147899Ssam    u_int16_t	pgmp_reserved;
94147899Ssam    /* ... u_int8_t	pgmp_nla[0]; */
95147899Ssam    /* ... options */
96147899Ssam};
97147899Ssam
98147899Ssamstruct pgm_data {
99147899Ssam    u_int32_t	pgmd_seq;
100147899Ssam    u_int32_t	pgmd_trailseq;
101147899Ssam    /* ... options */
102147899Ssam};
103147899Ssam
104147899Ssamtypedef enum _pgm_type {
105147899Ssam    PGM_SPM = 0,		/* source path message */
106147899Ssam    PGM_POLL = 1,		/* POLL Request */
107147899Ssam    PGM_POLR = 2,		/* POLL Response */
108147899Ssam    PGM_ODATA = 4,		/* original data */
109147899Ssam    PGM_RDATA = 5,		/* repair data */
110147899Ssam    PGM_NAK = 8,		/* NAK */
111147899Ssam    PGM_NULLNAK = 9,		/* Null NAK */
112147899Ssam    PGM_NCF = 10,		/* NAK Confirmation */
113147899Ssam    PGM_ACK = 11,		/* ACK for congestion control */
114147899Ssam    PGM_SPMR = 12,		/* SPM request */
115147899Ssam    PGM_MAX = 255
116147899Ssam} pgm_type;
117147899Ssam
118147899Ssam#define PGM_OPT_BIT_PRESENT	0x01
119147899Ssam#define PGM_OPT_BIT_NETWORK	0x02
120147899Ssam#define PGM_OPT_BIT_VAR_PKTLEN	0x40
121147899Ssam#define PGM_OPT_BIT_PARITY	0x80
122147899Ssam
123147899Ssam#define PGM_OPT_LENGTH		0x00
124147899Ssam#define PGM_OPT_FRAGMENT        0x01
125147899Ssam#define PGM_OPT_NAK_LIST        0x02
126147899Ssam#define PGM_OPT_JOIN            0x03
127147899Ssam#define PGM_OPT_NAK_BO_IVL	0x04
128147899Ssam#define PGM_OPT_NAK_BO_RNG	0x05
129147899Ssam
130147899Ssam#define PGM_OPT_REDIRECT        0x07
131147899Ssam#define PGM_OPT_PARITY_PRM      0x08
132147899Ssam#define PGM_OPT_PARITY_GRP      0x09
133147899Ssam#define PGM_OPT_CURR_TGSIZE     0x0A
134147899Ssam#define PGM_OPT_NBR_UNREACH	0x0B
135147899Ssam#define PGM_OPT_PATH_NLA	0x0C
136147899Ssam
137147899Ssam#define PGM_OPT_SYN             0x0D
138147899Ssam#define PGM_OPT_FIN             0x0E
139147899Ssam#define PGM_OPT_RST             0x0F
140147899Ssam#define PGM_OPT_CR		0x10
141147899Ssam#define PGM_OPT_CRQST		0x11
142236192Sdelphij
143236192Sdelphij#define PGM_OPT_PGMCC_DATA	0x12
144236192Sdelphij#define PGM_OPT_PGMCC_FEEDBACK	0x13
145147899Ssam
146147899Ssam#define PGM_OPT_MASK		0x7f
147147899Ssam
148147899Ssam#define PGM_OPT_END		0x80    /* end of options marker */
149147899Ssam
150147899Ssam#define PGM_MIN_OPT_LEN		4
151147899Ssam
152147899Ssam#ifndef AFI_IP
153147899Ssam#define AFI_IP		1
154147899Ssam#define AFI_IP6	        2
155147899Ssam#endif
156147899Ssam
157147899Ssamvoid
158147899Ssampgm_print(register const u_char *bp, register u_int length,
159147899Ssam	  register const u_char *bp2)
160147899Ssam{
161147899Ssam	register const struct pgm_header *pgm;
162147899Ssam	register const struct ip *ip;
163147899Ssam	register char ch;
164147899Ssam	u_int16_t sport, dport;
165147899Ssam	int addr_size;
166147899Ssam	const void *nla;
167147899Ssam	int nla_af;
168147899Ssam#ifdef INET6
169147899Ssam	char nla_buf[INET6_ADDRSTRLEN];
170147899Ssam	register const struct ip6_hdr *ip6;
171147899Ssam#else
172147899Ssam	char nla_buf[INET_ADDRSTRLEN];
173147899Ssam#endif
174147899Ssam	u_int8_t opt_type, opt_len, flags1, flags2;
175147899Ssam	u_int32_t seq, opts_len, len, offset;
176147899Ssam
177147899Ssam	pgm = (struct pgm_header *)bp;
178147899Ssam	ip = (struct ip *)bp2;
179147899Ssam#ifdef INET6
180147899Ssam	if (IP_V(ip) == 6)
181147899Ssam		ip6 = (struct ip6_hdr *)bp2;
182147899Ssam	else
183147899Ssam		ip6 = NULL;
184147899Ssam#else /* INET6 */
185147899Ssam	if (IP_V(ip) == 6) {
186147899Ssam		(void)printf("Can't handle IPv6");
187147899Ssam		return;
188147899Ssam	}
189147899Ssam#endif /* INET6 */
190147899Ssam	ch = '\0';
191147899Ssam	if (!TTEST(pgm->pgm_dport)) {
192147899Ssam#ifdef INET6
193147899Ssam		if (ip6) {
194147899Ssam			(void)printf("%s > %s: [|pgm]",
195147899Ssam				ip6addr_string(&ip6->ip6_src),
196147899Ssam				ip6addr_string(&ip6->ip6_dst));
197147899Ssam			return;
198147899Ssam		} else
199147899Ssam#endif /* INET6 */
200147899Ssam		{
201147899Ssam			(void)printf("%s > %s: [|pgm]",
202147899Ssam				ipaddr_string(&ip->ip_src),
203147899Ssam				ipaddr_string(&ip->ip_dst));
204147899Ssam			return;
205147899Ssam		}
206147899Ssam	}
207147899Ssam
208147899Ssam	sport = EXTRACT_16BITS(&pgm->pgm_sport);
209147899Ssam	dport = EXTRACT_16BITS(&pgm->pgm_dport);
210147899Ssam
211147899Ssam#ifdef INET6
212147899Ssam	if (ip6) {
213147899Ssam		if (ip6->ip6_nxt == IPPROTO_PGM) {
214147899Ssam			(void)printf("%s.%s > %s.%s: ",
215147899Ssam				ip6addr_string(&ip6->ip6_src),
216147899Ssam				tcpport_string(sport),
217147899Ssam				ip6addr_string(&ip6->ip6_dst),
218147899Ssam				tcpport_string(dport));
219147899Ssam		} else {
220147899Ssam			(void)printf("%s > %s: ",
221147899Ssam				tcpport_string(sport), tcpport_string(dport));
222147899Ssam		}
223147899Ssam	} else
224147899Ssam#endif /*INET6*/
225147899Ssam	{
226147899Ssam		if (ip->ip_p == IPPROTO_PGM) {
227147899Ssam			(void)printf("%s.%s > %s.%s: ",
228147899Ssam				ipaddr_string(&ip->ip_src),
229147899Ssam				tcpport_string(sport),
230147899Ssam				ipaddr_string(&ip->ip_dst),
231147899Ssam				tcpport_string(dport));
232147899Ssam		} else {
233147899Ssam			(void)printf("%s > %s: ",
234147899Ssam				tcpport_string(sport), tcpport_string(dport));
235147899Ssam		}
236147899Ssam	}
237147899Ssam
238147899Ssam	TCHECK(*pgm);
239147899Ssam
240147899Ssam        (void)printf("PGM, length %u", pgm->pgm_length);
241147899Ssam
242147899Ssam        if (!vflag)
243147899Ssam            return;
244147899Ssam
245147899Ssam        if (length > pgm->pgm_length)
246147899Ssam            length = pgm->pgm_length;
247147899Ssam
248147899Ssam	(void)printf(" 0x%02x%02x%02x%02x%02x%02x ",
249147899Ssam		     pgm->pgm_gsid[0],
250147899Ssam                     pgm->pgm_gsid[1],
251147899Ssam                     pgm->pgm_gsid[2],
252147899Ssam		     pgm->pgm_gsid[3],
253147899Ssam                     pgm->pgm_gsid[4],
254147899Ssam                     pgm->pgm_gsid[5]);
255147899Ssam	switch (pgm->pgm_type) {
256147899Ssam	case PGM_SPM: {
257147899Ssam	    struct pgm_spm *spm;
258147899Ssam
259147899Ssam	    spm = (struct pgm_spm *)(pgm + 1);
260147899Ssam	    TCHECK(*spm);
261147899Ssam
262147899Ssam	    switch (EXTRACT_16BITS(&spm->pgms_nla_afi)) {
263147899Ssam	    case AFI_IP:
264147899Ssam		addr_size = sizeof(struct in_addr);
265147899Ssam		nla_af = AF_INET;
266147899Ssam		break;
267147899Ssam#ifdef INET6
268147899Ssam	    case AFI_IP6:
269147899Ssam		addr_size = sizeof(struct in6_addr);
270147899Ssam		nla_af = AF_INET6;
271147899Ssam		break;
272147899Ssam#endif
273147899Ssam	    default:
274147899Ssam		goto trunc;
275147899Ssam		break;
276147899Ssam	    }
277147899Ssam	    bp = (u_char *) (spm + 1);
278147899Ssam	    TCHECK2(*bp, addr_size);
279147899Ssam	    nla = bp;
280147899Ssam	    bp += addr_size;
281147899Ssam
282147899Ssam	    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
283147899Ssam	    (void)printf("SPM seq %u trail %u lead %u nla %s",
284147899Ssam			 EXTRACT_32BITS(&spm->pgms_seq),
285147899Ssam                         EXTRACT_32BITS(&spm->pgms_trailseq),
286147899Ssam			 EXTRACT_32BITS(&spm->pgms_leadseq),
287147899Ssam                         nla_buf);
288147899Ssam	    break;
289147899Ssam	}
290147899Ssam
291147899Ssam	case PGM_POLL: {
292147899Ssam	    struct pgm_poll *poll;
293147899Ssam
294147899Ssam	    poll = (struct pgm_poll *)(pgm + 1);
295147899Ssam	    TCHECK(*poll);
296147899Ssam	    (void)printf("POLL seq %u round %u",
297147899Ssam			 EXTRACT_32BITS(&poll->pgmp_seq),
298147899Ssam                         EXTRACT_16BITS(&poll->pgmp_round));
299147899Ssam	    bp = (u_char *) (poll + 1);
300147899Ssam	    break;
301147899Ssam	}
302147899Ssam	case PGM_POLR: {
303147899Ssam	    struct pgm_polr *polr;
304147899Ssam	    u_int32_t ivl, rnd, mask;
305147899Ssam
306147899Ssam	    polr = (struct pgm_polr *)(pgm + 1);
307147899Ssam	    TCHECK(*polr);
308147899Ssam
309147899Ssam	    switch (EXTRACT_16BITS(&polr->pgmp_nla_afi)) {
310147899Ssam	    case AFI_IP:
311147899Ssam		addr_size = sizeof(struct in_addr);
312147899Ssam		nla_af = AF_INET;
313147899Ssam		break;
314147899Ssam#ifdef INET6
315147899Ssam	    case AFI_IP6:
316147899Ssam		addr_size = sizeof(struct in6_addr);
317147899Ssam		nla_af = AF_INET6;
318147899Ssam		break;
319147899Ssam#endif
320147899Ssam	    default:
321147899Ssam		goto trunc;
322147899Ssam		break;
323147899Ssam	    }
324147899Ssam	    bp = (u_char *) (polr + 1);
325147899Ssam	    TCHECK2(*bp, addr_size);
326147899Ssam	    nla = bp;
327147899Ssam	    bp += addr_size;
328147899Ssam
329147899Ssam	    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
330147899Ssam
331147899Ssam	    TCHECK2(*bp, sizeof(u_int32_t));
332147899Ssam	    ivl = EXTRACT_32BITS(bp);
333147899Ssam	    bp += sizeof(u_int32_t);
334147899Ssam
335147899Ssam	    TCHECK2(*bp, sizeof(u_int32_t));
336147899Ssam	    rnd = EXTRACT_32BITS(bp);
337147899Ssam	    bp += sizeof(u_int32_t);
338147899Ssam
339147899Ssam	    TCHECK2(*bp, sizeof(u_int32_t));
340147899Ssam	    mask = EXTRACT_32BITS(bp);
341147899Ssam	    bp += sizeof(u_int32_t);
342147899Ssam
343147899Ssam	    (void)printf("POLR seq %u round %u nla %s ivl %u rnd 0x%08x "
344147899Ssam			 "mask 0x%08x", EXTRACT_32BITS(&polr->pgmp_seq),
345147899Ssam			 EXTRACT_16BITS(&polr->pgmp_round), nla_buf, ivl, rnd, mask);
346147899Ssam	    break;
347147899Ssam	}
348147899Ssam	case PGM_ODATA: {
349147899Ssam	    struct pgm_data *odata;
350147899Ssam
351147899Ssam	    odata = (struct pgm_data *)(pgm + 1);
352147899Ssam	    TCHECK(*odata);
353147899Ssam	    (void)printf("ODATA trail %u seq %u",
354147899Ssam			 EXTRACT_32BITS(&odata->pgmd_trailseq),
355147899Ssam			 EXTRACT_32BITS(&odata->pgmd_seq));
356147899Ssam	    bp = (u_char *) (odata + 1);
357147899Ssam	    break;
358147899Ssam	}
359147899Ssam
360147899Ssam	case PGM_RDATA: {
361147899Ssam	    struct pgm_data *rdata;
362147899Ssam
363147899Ssam	    rdata = (struct pgm_data *)(pgm + 1);
364147899Ssam	    TCHECK(*rdata);
365147899Ssam	    (void)printf("RDATA trail %u seq %u",
366147899Ssam			 EXTRACT_32BITS(&rdata->pgmd_trailseq),
367147899Ssam			 EXTRACT_32BITS(&rdata->pgmd_seq));
368147899Ssam	    bp = (u_char *) (rdata + 1);
369147899Ssam	    break;
370147899Ssam	}
371147899Ssam
372147899Ssam	case PGM_NAK:
373147899Ssam	case PGM_NULLNAK:
374147899Ssam	case PGM_NCF: {
375147899Ssam	    struct pgm_nak *nak;
376147899Ssam	    const void *source, *group;
377147899Ssam	    int source_af, group_af;
378147899Ssam#ifdef INET6
379147899Ssam	    char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
380147899Ssam#else
381147899Ssam	    char source_buf[INET_ADDRSTRLEN], group_buf[INET_ADDRSTRLEN];
382147899Ssam#endif
383147899Ssam
384147899Ssam	    nak = (struct pgm_nak *)(pgm + 1);
385147899Ssam	    TCHECK(*nak);
386147899Ssam
387147899Ssam	    /*
388147899Ssam	     * Skip past the source, saving info along the way
389147899Ssam	     * and stopping if we don't have enough.
390147899Ssam	     */
391147899Ssam	    switch (EXTRACT_16BITS(&nak->pgmn_source_afi)) {
392147899Ssam	    case AFI_IP:
393147899Ssam		addr_size = sizeof(struct in_addr);
394147899Ssam		source_af = AF_INET;
395147899Ssam		break;
396147899Ssam#ifdef INET6
397147899Ssam	    case AFI_IP6:
398147899Ssam		addr_size = sizeof(struct in6_addr);
399147899Ssam		source_af = AF_INET6;
400147899Ssam		break;
401147899Ssam#endif
402147899Ssam	    default:
403147899Ssam		goto trunc;
404147899Ssam		break;
405147899Ssam	    }
406147899Ssam	    bp = (u_char *) (nak + 1);
407147899Ssam	    TCHECK2(*bp, addr_size);
408147899Ssam	    source = bp;
409147899Ssam	    bp += addr_size;
410147899Ssam
411147899Ssam	    /*
412147899Ssam	     * Skip past the group, saving info along the way
413147899Ssam	     * and stopping if we don't have enough.
414147899Ssam	     */
415147899Ssam	    switch (EXTRACT_16BITS(bp)) {
416147899Ssam	    case AFI_IP:
417147899Ssam		addr_size = sizeof(struct in_addr);
418147899Ssam		group_af = AF_INET;
419147899Ssam		break;
420147899Ssam#ifdef INET6
421147899Ssam	    case AFI_IP6:
422147899Ssam		addr_size = sizeof(struct in6_addr);
423147899Ssam		group_af = AF_INET6;
424147899Ssam		break;
425147899Ssam#endif
426147899Ssam	    default:
427147899Ssam		goto trunc;
428147899Ssam		break;
429147899Ssam	    }
430147899Ssam	    bp += (2 * sizeof(u_int16_t));
431147899Ssam	    TCHECK2(*bp, addr_size);
432147899Ssam	    group = bp;
433147899Ssam	    bp += addr_size;
434147899Ssam
435147899Ssam	    /*
436147899Ssam	     * Options decoding can go here.
437147899Ssam	     */
438147899Ssam	    inet_ntop(source_af, source, source_buf, sizeof(source_buf));
439147899Ssam	    inet_ntop(group_af, group, group_buf, sizeof(group_buf));
440147899Ssam	    switch (pgm->pgm_type) {
441147899Ssam		case PGM_NAK:
442147899Ssam		    (void)printf("NAK ");
443147899Ssam		    break;
444147899Ssam		case PGM_NULLNAK:
445147899Ssam		    (void)printf("NNAK ");
446147899Ssam		    break;
447147899Ssam		case PGM_NCF:
448147899Ssam		    (void)printf("NCF ");
449147899Ssam		    break;
450147899Ssam		default:
451147899Ssam                    break;
452147899Ssam	    }
453147899Ssam	    (void)printf("(%s -> %s), seq %u",
454147899Ssam			 source_buf, group_buf, EXTRACT_32BITS(&nak->pgmn_seq));
455147899Ssam	    break;
456147899Ssam	}
457147899Ssam
458236192Sdelphij	case PGM_ACK: {
459236192Sdelphij	    struct pgm_ack *ack;
460236192Sdelphij
461236192Sdelphij	    ack = (struct pgm_ack *)(pgm + 1);
462236192Sdelphij	    TCHECK(*ack);
463236192Sdelphij	    (void)printf("ACK seq %u",
464236192Sdelphij			 EXTRACT_32BITS(&ack->pgma_rx_max_seq));
465236192Sdelphij	    bp = (u_char *) (ack + 1);
466236192Sdelphij	    break;
467236192Sdelphij	}
468236192Sdelphij
469147899Ssam	case PGM_SPMR:
470147899Ssam	    (void)printf("SPMR");
471147899Ssam	    break;
472147899Ssam
473147899Ssam	default:
474147899Ssam	    (void)printf("UNKNOWN type %0x02x", pgm->pgm_type);
475147899Ssam	    break;
476147899Ssam
477147899Ssam	}
478147899Ssam	if (pgm->pgm_options & PGM_OPT_BIT_PRESENT) {
479147899Ssam
480147899Ssam	    /*
481147899Ssam	     * make sure there's enough for the first option header
482147899Ssam	     */
483147899Ssam	    if (!TTEST2(*bp, PGM_MIN_OPT_LEN)) {
484147899Ssam		(void)printf("[|OPT]");
485147899Ssam		return;
486147899Ssam	    }
487147899Ssam
488147899Ssam	    /*
489147899Ssam	     * That option header MUST be an OPT_LENGTH option
490147899Ssam	     * (see the first paragraph of section 9.1 in RFC 3208).
491147899Ssam	     */
492147899Ssam	    opt_type = *bp++;
493147899Ssam	    if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
494147899Ssam		(void)printf("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
495147899Ssam		return;
496147899Ssam	    }
497147899Ssam	    opt_len = *bp++;
498147899Ssam	    if (opt_len != 4) {
499147899Ssam		(void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
500147899Ssam		return;
501147899Ssam	    }
502147899Ssam	    opts_len = EXTRACT_16BITS(bp);
503147899Ssam	    if (opts_len < 4) {
504147899Ssam		(void)printf("[Bad total option length %u < 4]", opts_len);
505147899Ssam		return;
506147899Ssam	    }
507147899Ssam	    bp += sizeof(u_int16_t);
508147899Ssam	    (void)printf(" OPTS LEN %d", opts_len);
509147899Ssam	    opts_len -= 4;
510147899Ssam
511147899Ssam	    while (opts_len) {
512147899Ssam		if (opts_len < PGM_MIN_OPT_LEN) {
513147899Ssam		    (void)printf("[Total option length leaves no room for final option]");
514147899Ssam		    return;
515147899Ssam		}
516147899Ssam		opt_type = *bp++;
517147899Ssam		opt_len = *bp++;
518147899Ssam		if (opt_len < PGM_MIN_OPT_LEN) {
519147899Ssam		    (void)printf("[Bad option, length %u < %u]", opt_len,
520147899Ssam		        PGM_MIN_OPT_LEN);
521147899Ssam		    break;
522147899Ssam		}
523147899Ssam		if (opts_len < opt_len) {
524147899Ssam		    (void)printf("[Total option length leaves no room for final option]");
525147899Ssam		    return;
526147899Ssam		}
527147899Ssam		if (!TTEST2(*bp, opt_len - 2)) {
528147899Ssam		    (void)printf(" [|OPT]");
529147899Ssam		    return;
530147899Ssam		}
531147899Ssam
532147899Ssam		switch (opt_type & PGM_OPT_MASK) {
533147899Ssam		case PGM_OPT_LENGTH:
534147899Ssam		    if (opt_len != 4) {
535147899Ssam			(void)printf("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
536147899Ssam			return;
537147899Ssam		    }
538147899Ssam		    (void)printf(" OPTS LEN (extra?) %d", EXTRACT_16BITS(bp));
539147899Ssam		    bp += sizeof(u_int16_t);
540147899Ssam		    opts_len -= 4;
541147899Ssam		    break;
542147899Ssam
543147899Ssam		case PGM_OPT_FRAGMENT:
544147899Ssam		    if (opt_len != 16) {
545147899Ssam			(void)printf("[Bad OPT_FRAGMENT option, length %u != 16]", opt_len);
546147899Ssam			return;
547147899Ssam		    }
548147899Ssam		    flags1 = *bp++;
549147899Ssam		    flags2 = *bp++;
550147899Ssam		    seq = EXTRACT_32BITS(bp);
551147899Ssam		    bp += sizeof(u_int32_t);
552147899Ssam		    offset = EXTRACT_32BITS(bp);
553147899Ssam		    bp += sizeof(u_int32_t);
554147899Ssam		    len = EXTRACT_32BITS(bp);
555147899Ssam		    bp += sizeof(u_int32_t);
556147899Ssam		    (void)printf(" FRAG seq %u off %u len %u", seq, offset, len);
557147899Ssam		    opts_len -= 16;
558147899Ssam		    break;
559147899Ssam
560147899Ssam		case PGM_OPT_NAK_LIST:
561147899Ssam		    flags1 = *bp++;
562147899Ssam		    flags2 = *bp++;
563147899Ssam		    opt_len -= sizeof(u_int32_t);	/* option header */
564147899Ssam		    (void)printf(" NAK LIST");
565147899Ssam		    while (opt_len) {
566147899Ssam			if (opt_len < sizeof(u_int32_t)) {
567147899Ssam			    (void)printf("[Option length not a multiple of 4]");
568147899Ssam			    return;
569147899Ssam			}
570147899Ssam			TCHECK2(*bp, sizeof(u_int32_t));
571147899Ssam			(void)printf(" %u", EXTRACT_32BITS(bp));
572147899Ssam			bp += sizeof(u_int32_t);
573147899Ssam			opt_len -= sizeof(u_int32_t);
574147899Ssam			opts_len -= sizeof(u_int32_t);
575147899Ssam		    }
576147899Ssam		    break;
577147899Ssam
578147899Ssam		case PGM_OPT_JOIN:
579147899Ssam		    if (opt_len != 8) {
580147899Ssam			(void)printf("[Bad OPT_JOIN option, length %u != 8]", opt_len);
581147899Ssam			return;
582147899Ssam		    }
583147899Ssam		    flags1 = *bp++;
584147899Ssam		    flags2 = *bp++;
585147899Ssam		    seq = EXTRACT_32BITS(bp);
586147899Ssam		    bp += sizeof(u_int32_t);
587147899Ssam		    (void)printf(" JOIN %u", seq);
588147899Ssam		    opts_len -= 8;
589147899Ssam		    break;
590147899Ssam
591147899Ssam		case PGM_OPT_NAK_BO_IVL:
592147899Ssam		    if (opt_len != 12) {
593147899Ssam			(void)printf("[Bad OPT_NAK_BO_IVL option, length %u != 12]", opt_len);
594147899Ssam			return;
595147899Ssam		    }
596147899Ssam		    flags1 = *bp++;
597147899Ssam		    flags2 = *bp++;
598147899Ssam		    offset = EXTRACT_32BITS(bp);
599147899Ssam		    bp += sizeof(u_int32_t);
600147899Ssam		    seq = EXTRACT_32BITS(bp);
601147899Ssam		    bp += sizeof(u_int32_t);
602147899Ssam		    (void)printf(" BACKOFF ivl %u ivlseq %u", offset, seq);
603147899Ssam		    opts_len -= 12;
604147899Ssam		    break;
605147899Ssam
606147899Ssam		case PGM_OPT_NAK_BO_RNG:
607147899Ssam		    if (opt_len != 12) {
608147899Ssam			(void)printf("[Bad OPT_NAK_BO_RNG option, length %u != 12]", opt_len);
609147899Ssam			return;
610147899Ssam		    }
611147899Ssam		    flags1 = *bp++;
612147899Ssam		    flags2 = *bp++;
613147899Ssam		    offset = EXTRACT_32BITS(bp);
614147899Ssam		    bp += sizeof(u_int32_t);
615147899Ssam		    seq = EXTRACT_32BITS(bp);
616147899Ssam		    bp += sizeof(u_int32_t);
617147899Ssam		    (void)printf(" BACKOFF max %u min %u", offset, seq);
618147899Ssam		    opts_len -= 12;
619147899Ssam		    break;
620147899Ssam
621147899Ssam		case PGM_OPT_REDIRECT:
622147899Ssam		    flags1 = *bp++;
623147899Ssam		    flags2 = *bp++;
624147899Ssam		    switch (EXTRACT_16BITS(bp)) {
625147899Ssam		    case AFI_IP:
626147899Ssam			addr_size = sizeof(struct in_addr);
627147899Ssam			nla_af = AF_INET;
628147899Ssam			break;
629147899Ssam#ifdef INET6
630147899Ssam		    case AFI_IP6:
631147899Ssam			addr_size = sizeof(struct in6_addr);
632147899Ssam			nla_af = AF_INET6;
633147899Ssam			break;
634147899Ssam#endif
635147899Ssam		    default:
636147899Ssam			goto trunc;
637147899Ssam			break;
638147899Ssam		    }
639147899Ssam		    bp += (2 * sizeof(u_int16_t));
640147899Ssam		    if (opt_len != 4 + addr_size) {
641147899Ssam			(void)printf("[Bad OPT_REDIRECT option, length %u != 4 + address size]", opt_len);
642147899Ssam			return;
643147899Ssam		    }
644147899Ssam		    TCHECK2(*bp, addr_size);
645147899Ssam		    nla = bp;
646147899Ssam		    bp += addr_size;
647147899Ssam
648147899Ssam		    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
649147899Ssam		    (void)printf(" REDIRECT %s",  (char *)nla);
650147899Ssam		    opts_len -= 4 + addr_size;
651147899Ssam		    break;
652147899Ssam
653147899Ssam		case PGM_OPT_PARITY_PRM:
654147899Ssam		    if (opt_len != 8) {
655147899Ssam			(void)printf("[Bad OPT_PARITY_PRM option, length %u != 8]", opt_len);
656147899Ssam			return;
657147899Ssam		    }
658147899Ssam		    flags1 = *bp++;
659147899Ssam		    flags2 = *bp++;
660147899Ssam		    len = EXTRACT_32BITS(bp);
661147899Ssam		    bp += sizeof(u_int32_t);
662147899Ssam		    (void)printf(" PARITY MAXTGS %u", len);
663147899Ssam		    opts_len -= 8;
664147899Ssam		    break;
665147899Ssam
666147899Ssam		case PGM_OPT_PARITY_GRP:
667147899Ssam		    if (opt_len != 8) {
668147899Ssam			(void)printf("[Bad OPT_PARITY_GRP option, length %u != 8]", opt_len);
669147899Ssam			return;
670147899Ssam		    }
671147899Ssam		    flags1 = *bp++;
672147899Ssam		    flags2 = *bp++;
673147899Ssam		    seq = EXTRACT_32BITS(bp);
674147899Ssam		    bp += sizeof(u_int32_t);
675147899Ssam		    (void)printf(" PARITY GROUP %u", seq);
676147899Ssam		    opts_len -= 8;
677147899Ssam		    break;
678147899Ssam
679147899Ssam		case PGM_OPT_CURR_TGSIZE:
680147899Ssam		    if (opt_len != 8) {
681147899Ssam			(void)printf("[Bad OPT_CURR_TGSIZE option, length %u != 8]", opt_len);
682147899Ssam			return;
683147899Ssam		    }
684147899Ssam		    flags1 = *bp++;
685147899Ssam		    flags2 = *bp++;
686147899Ssam		    len = EXTRACT_32BITS(bp);
687147899Ssam		    bp += sizeof(u_int32_t);
688147899Ssam		    (void)printf(" PARITY ATGS %u", len);
689147899Ssam		    opts_len -= 8;
690147899Ssam		    break;
691147899Ssam
692147899Ssam		case PGM_OPT_NBR_UNREACH:
693147899Ssam		    if (opt_len != 4) {
694147899Ssam			(void)printf("[Bad OPT_NBR_UNREACH option, length %u != 4]", opt_len);
695147899Ssam			return;
696147899Ssam		    }
697147899Ssam		    flags1 = *bp++;
698147899Ssam		    flags2 = *bp++;
699147899Ssam		    (void)printf(" NBR_UNREACH");
700147899Ssam		    opts_len -= 4;
701147899Ssam		    break;
702147899Ssam
703147899Ssam		case PGM_OPT_PATH_NLA:
704147899Ssam		    (void)printf(" PATH_NLA [%d]", opt_len);
705147899Ssam		    bp += opt_len;
706147899Ssam		    opts_len -= opt_len;
707147899Ssam		    break;
708147899Ssam
709147899Ssam		case PGM_OPT_SYN:
710147899Ssam		    if (opt_len != 4) {
711147899Ssam			(void)printf("[Bad OPT_SYN option, length %u != 4]", opt_len);
712147899Ssam			return;
713147899Ssam		    }
714147899Ssam		    flags1 = *bp++;
715147899Ssam		    flags2 = *bp++;
716147899Ssam		    (void)printf(" SYN");
717147899Ssam		    opts_len -= 4;
718147899Ssam		    break;
719147899Ssam
720147899Ssam		case PGM_OPT_FIN:
721147899Ssam		    if (opt_len != 4) {
722147899Ssam			(void)printf("[Bad OPT_FIN option, length %u != 4]", opt_len);
723147899Ssam			return;
724147899Ssam		    }
725147899Ssam		    flags1 = *bp++;
726147899Ssam		    flags2 = *bp++;
727147899Ssam		    (void)printf(" FIN");
728147899Ssam		    opts_len -= 4;
729147899Ssam		    break;
730147899Ssam
731147899Ssam		case PGM_OPT_RST:
732147899Ssam		    if (opt_len != 4) {
733147899Ssam			(void)printf("[Bad OPT_RST option, length %u != 4]", opt_len);
734147899Ssam			return;
735147899Ssam		    }
736147899Ssam		    flags1 = *bp++;
737147899Ssam		    flags2 = *bp++;
738147899Ssam		    (void)printf(" RST");
739147899Ssam		    opts_len -= 4;
740147899Ssam		    break;
741147899Ssam
742147899Ssam		case PGM_OPT_CR:
743147899Ssam		    (void)printf(" CR");
744147899Ssam		    bp += opt_len;
745147899Ssam		    opts_len -= opt_len;
746147899Ssam		    break;
747147899Ssam
748147899Ssam		case PGM_OPT_CRQST:
749147899Ssam		    if (opt_len != 4) {
750147899Ssam			(void)printf("[Bad OPT_CRQST option, length %u != 4]", opt_len);
751147899Ssam			return;
752147899Ssam		    }
753147899Ssam		    flags1 = *bp++;
754147899Ssam		    flags2 = *bp++;
755147899Ssam		    (void)printf(" CRQST");
756147899Ssam		    opts_len -= 4;
757147899Ssam		    break;
758147899Ssam
759236192Sdelphij		case PGM_OPT_PGMCC_DATA:
760236192Sdelphij		    flags1 = *bp++;
761236192Sdelphij		    flags2 = *bp++;
762236192Sdelphij		    offset = EXTRACT_32BITS(bp);
763236192Sdelphij		    bp += sizeof(u_int32_t);
764236192Sdelphij		    switch (EXTRACT_16BITS(bp)) {
765236192Sdelphij		    case AFI_IP:
766236192Sdelphij			addr_size = sizeof(struct in_addr);
767236192Sdelphij			nla_af = AF_INET;
768236192Sdelphij			break;
769236192Sdelphij#ifdef INET6
770236192Sdelphij		    case AFI_IP6:
771236192Sdelphij			addr_size = sizeof(struct in6_addr);
772236192Sdelphij			nla_af = AF_INET6;
773236192Sdelphij			break;
774236192Sdelphij#endif
775236192Sdelphij		    default:
776236192Sdelphij			goto trunc;
777236192Sdelphij			break;
778236192Sdelphij		    }
779236192Sdelphij		    bp += (2 * sizeof(u_int16_t));
780236192Sdelphij		    if (opt_len != 12 + addr_size) {
781236192Sdelphij			(void)printf("[Bad OPT_PGMCC_DATA option, length %u != 12 + address size]", opt_len);
782236192Sdelphij			return;
783236192Sdelphij		    }
784236192Sdelphij		    TCHECK2(*bp, addr_size);
785236192Sdelphij		    nla = bp;
786236192Sdelphij		    bp += addr_size;
787236192Sdelphij
788236192Sdelphij		    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
789236192Sdelphij		    (void)printf(" PGMCC DATA %u %s", offset, (char*)nla);
790236192Sdelphij		    opts_len -= 16;
791236192Sdelphij		    break;
792236192Sdelphij
793236192Sdelphij		case PGM_OPT_PGMCC_FEEDBACK:
794236192Sdelphij		    flags1 = *bp++;
795236192Sdelphij		    flags2 = *bp++;
796236192Sdelphij		    offset = EXTRACT_32BITS(bp);
797236192Sdelphij		    bp += sizeof(u_int32_t);
798236192Sdelphij		    switch (EXTRACT_16BITS(bp)) {
799236192Sdelphij		    case AFI_IP:
800236192Sdelphij			addr_size = sizeof(struct in_addr);
801236192Sdelphij			nla_af = AF_INET;
802236192Sdelphij			break;
803236192Sdelphij#ifdef INET6
804236192Sdelphij		    case AFI_IP6:
805236192Sdelphij			addr_size = sizeof(struct in6_addr);
806236192Sdelphij			nla_af = AF_INET6;
807236192Sdelphij			break;
808236192Sdelphij#endif
809236192Sdelphij		    default:
810236192Sdelphij			goto trunc;
811236192Sdelphij			break;
812236192Sdelphij		    }
813236192Sdelphij		    bp += (2 * sizeof(u_int16_t));
814236192Sdelphij		    if (opt_len != 12 + addr_size) {
815236192Sdelphij			(void)printf("[Bad OPT_PGMCC_FEEDBACK option, length %u != 12 + address size]", opt_len);
816236192Sdelphij			return;
817236192Sdelphij		    }
818236192Sdelphij		    TCHECK2(*bp, addr_size);
819236192Sdelphij		    nla = bp;
820236192Sdelphij		    bp += addr_size;
821236192Sdelphij
822236192Sdelphij		    inet_ntop(nla_af, nla, nla_buf, sizeof(nla_buf));
823236192Sdelphij		    (void)printf(" PGMCC FEEDBACK %u %s", offset, (char*)nla);
824236192Sdelphij		    opts_len -= 16;
825236192Sdelphij		    break;
826236192Sdelphij
827147899Ssam		default:
828147899Ssam		    (void)printf(" OPT_%02X [%d] ", opt_type, opt_len);
829147899Ssam		    bp += opt_len;
830147899Ssam		    opts_len -= opt_len;
831147899Ssam		    break;
832147899Ssam		}
833147899Ssam
834147899Ssam		if (opt_type & PGM_OPT_END)
835147899Ssam		    break;
836147899Ssam	     }
837147899Ssam	}
838147899Ssam
839147899Ssam	(void)printf(" [%u]", EXTRACT_16BITS(&pgm->pgm_length));
840147899Ssam
841147899Ssam	return;
842147899Ssam
843147899Ssamtrunc:
844147899Ssam	fputs("[|pgm]", stdout);
845147899Ssam	if (ch != '\0')
846147899Ssam		putchar('>');
847147899Ssam}
848