/*
 * stpdump.c
 *
 * Print IEEE 802.1d Spanning Tree Protocol packets
 * Steve Buer <sabuer@syr.edu>
 *
 * Most of this code is stolen from tcpdump's print-stp.c
 *
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License    
 * as published by the Free Software Foundation; either version  
 * 2 of the License, or (at your option) any later version.    
 *
 * $Id: stpdump.c,v 1.2 2001/10/21 01:07:49 buer Exp $
 *
 */

#include <pcap.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>

#define	ETHER_TYPE_8021D 0x42

struct option longopts[] = {

        {"ether",	0, NULL, 0},
        {"help", 	0, NULL, 0},
        {"interface", 	1, NULL, 0},
        {"verbose", 	0, NULL, 0},
        {0}

};

void usage()
{

        fprintf(stderr, "Usage: stpdump [-h] [-v] [-e] [-i] \n");
        fprintf(stderr, "       --verbose       -v\n");
        fprintf(stderr, "       --ether         -e\n");
        fprintf(stderr, "       --interface     -i\n");
        fprintf(stderr, "       --help          -h\n");

}

char buf[sizeof("00:00:00:00:00:00")];
int ether = 0;

void print_bpdu(u_char *, const struct pcap_pkthdr *, const u_char *);
char *print_ether(const u_char *);

int main(int argc, char **argv)
{

	int sockfd, c;
	int errflg		= 0;
	int verbose		= 0;
	int theSnaplen    	= 128;
	char *theInterface 	= NULL;
	char ebuf[128];
	pcap_t *pcap;
	u_char *pcap_userdata;

	while ((c = getopt_long(argc, argv, "ehi:v", longopts, 0)) != EOF) {


                switch (c) {

                        case 'v':

                                verbose++;
                                break;

                        case 'h':

                                errflg++;
                                break;

			case 'e':

				ether++;
				break;

                        case 'i':

                                theInterface = optarg;
                                break;

                        default:

                                errflg++;
                                break;

		} //switch

	} //while

	if (!theInterface) {

                if (!(theInterface = pcap_lookupdev(ebuf))) {

                        fprintf(stderr, "pcap_lookupdev(): %s\n:", ebuf);
                        exit(1);

                }

        }

	printf("Listening on: %s\n", theInterface);

	if ((pcap = pcap_open_live(theInterface, theSnaplen, 1, 0, ebuf)) == NULL) {

                fprintf(stderr, "pcap_open_live(): %s\n", ebuf);
                exit(1);

        }

	if (pcap_datalink(pcap) != DLT_EN10MB) {

                fprintf(stderr, "Link layer must be Ethernet.\n");
                exit(1);
        
        }

	if (pcap_loop(pcap, -1, print_bpdu, pcap_userdata) < 0) {

                fprintf(stderr, "pcap_loop(): %s\n", pcap_geterr(pcap));
                exit(1);

        }

	return 0;

}

////////////////////////////////////////////
//
// Callback function to print STP packets
// 

void print_bpdu(u_char *user, const struct pcap_pkthdr *h, const u_char *p)
{

	u_char *bpdu;
	
	bpdu = (u_char *)p+14;

	if (!(p[13] & ETHER_TYPE_8021D))
		return;

	if (bpdu[2] != 0x03 || bpdu[3] || bpdu[4] || bpdu[5]) {

                printf("unknown version");
                return;

        }

	if (ether) {

		printf("%s ", print_ether(p));
		printf("%s ", print_ether(p+6));
		printf("%02x%02x ", *(p+12), *(p+13)); 

	}

	switch (p[6]) {
  
		case 0:
			
			/*
                
			if (length < 10) {

				printf("short packet\n");
				return;

			} */
                
			printf("config ");
                	break;

        	case 1:

                	printf("tcn \n");
			return;

        	default:

                	printf("unknown type %i\n", bpdu[6]);
                	break;

        } //switch

	if (bpdu[7] & 1)
                printf("TOP_CHANGE ");

        if (bpdu[7] & 0x80)
                printf("TOP_CHANGE_ACK ");

	printf("%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x.%.2x%.2x ",
               bpdu[20], bpdu[21], bpdu[22], bpdu[23], bpdu[24], bpdu[25], bpdu[26], bpdu[27], bpdu[28], bpdu[29]);

	printf("root ");

	printf("%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x ",
               bpdu[8], bpdu[9], bpdu[10], bpdu[11], bpdu[12], bpdu[13], bpdu[14], bpdu[15]);

	printf("pathcost %i ", (bpdu[16] << 24) | (bpdu[17] << 16) | (bpdu[18] << 8) | bpdu[19]);

	printf("age %i ", bpdu[30]);
        printf("max %i ", bpdu[32]);
        printf("hello %i ", bpdu[34]);
        printf("fdelay %i ", bpdu[36]);
	
	putchar('\n');

	return;

}

/////////////////////////////////////////////////
// Print ethernet addresses
//

char *print_ether(const u_char *ep)
{

        char hex[] = "0123456789abcdef";
        u_int i, j;
        char *cp;

        cp = buf;
	
	j = *ep >> 4;

        	*cp++ = hex[j];

        *cp++ = hex[*ep++ & 0xf];

        for (i = 5; (int)--i >= 0;) {

                *cp++ = ':';

	j = *ep >> 4;

                *cp++ = hex[j];

                *cp++ = hex[*ep++ & 0xf];

        }

        *cp = '\0';

        return buf;

}
