/*
 * Written 2001-02-12
 *
 * Copyright 2001 Vladislav V. Myasnyankin, Yekaterinburg Russia
 * hugevlad@yahoo.com http://www.geocities.com/hugevlad
 * All rights reserved.
 * 
 * WARNING! Be very careful, because you can occasionaly broke your network.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
 
#include <stdio.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/if_ether.h>
#include <sys/ioctl.h>
#include <sys/socket.h>

#define DEFAULT_DEVICE "eth0"
#define ETH_HW_ADDR_LEN 6

char usage[]={
"usage: stp \
[-v] \
[-dev <device>] \
[-dmac <dmac>] \
[-smac <smac>] \n\
-protoid <proto_id> \
-protovid <proto_v_id> \
-bpdu <bpdutype> \
-flags <flags> \n\
-rootid <rootid> \
-rootpc <rootpc> \
-brid <brid> \
-portid <portid> \
-mage <mage> \n\
-maxage <maxage> \
-htime <hellotime> \
-fdelay <fdelay> \n\
where:\n\
-v - be verbose and write output to file packet.dmp instead socket \n\
device - ethernet device name (default - eth0)\n\
dmac - destination MAC (default - 01:80:C2:00:00:00)\n\
smac - source MAC (default - MAC on given or default device)\n\
proto_id - Protocol Identifier (hex, 2 bytes)\n\
proto_v_id - Protocol Version Identifier (hex, 1 byte)\n\
bpdutype - BPDU type (hex, 1 byte)\n\
flags - flags value (hex, 1 byte)\n\
rootid - Root Identifier (hex, 8 bytes)\n\
rootpc - Root Path Cost (hex, 4 bytes)\n\
brid - Bridge Identifier (hex, 8 bytes)\n\
portid - Port Identifier (hex, 2 bytes)\n\
mage - Message Age (hex, 2 bytes)\n\
maxage - Max Age (hex, 2 bytes)\n\
hellotime - Hello Time (hex, 2 bytes)\n\
fdelay - Forward Delay (hex, 2 bytes)\n\
\n"};

struct bpdu_packet {
        u_char targ_hw_addr[ETH_HW_ADDR_LEN]; /* dest. ether address */
        u_char src_hw_addr[ETH_HW_ADDR_LEN];  /* src. ether address */
        u_char frame_type[2];                 /* 0x00 0x26 */
        u_char llc_dest;                      /* 0x42 */
        u_char llc_src;                       /* 0x42 */
        u_char unknown;                       /* 03 why? I don`t know :( */
        u_char proto_id[2];                   /* Protocol Identifier */
        u_char proto_v_id;                    /* Protocol Version Identifier */
        u_char bpdu_type;                     /* BPDU Type */
        u_char flags;                         /* Flags */
        u_char rootid[8];                     /* Root Identifier */
        u_char rootpc[4];                     /* Root Path Cost */
        u_char brid[8];                       /* Bridge Identifier */
        u_char portid[2];                     /* Port Identifier */
        u_char mage[2];                       /* Message Age */
        u_char maxage[2];                     /* Max Age */
        u_char hellotime[2];                  /* Hello Time */
        u_char fdelay[2];                     /* Forward Delay */
        u_char padding[8];                    /* padding packet to 60 bytes */
};

void fireexit(char *); /* print error message and exit with code 1*/
void get_hex_value(char*,char*,int); /* convert string to hex */


                           /**** MAIN PART *****/
int main(int argc,char** argv){

FILE *f;
struct bpdu_packet pkt1;
struct sockaddr saddr;
struct ifreq ifr;
int sd,i;
char eth_dev[30];
unsigned char verbose=0;
unsigned int complete=0;
/*
bites array:
14 device
13 dmac
12 smac
11 proto_id
10 proto_v_id
9 bpdutype
8 flags
7 rootid
6 rootpc
5 brid
4 portid
3 mage
2 maxage
1 hellotime
0 fdelay
*/


strncpy(eth_dev,DEFAULT_DEVICE,29);

for(i=1; i<argc; i++){
   if (!strncasecmp(argv[i],"-v",2)) verbose=1;
   if (!strncasecmp(argv[i],"-dev",4)){
      if (verbose) printf("Device: ");
      strncpy(eth_dev,argv[++i],29);
      if (verbose) printf("Ok\n");
      complete|=0x4000;
      };
   if (!strncasecmp(argv[i],"-dmac",5)){
      if (verbose) printf("Destination MAC: ");
      get_hex_value(pkt1.targ_hw_addr,argv[++i],6);
      if (verbose) printf("Ok\n");
      complete|=0x2000;
      };
   if (!strncasecmp(argv[i],"-smac",5)){
      if (verbose) printf("Source MAC: ");
      get_hex_value(pkt1.src_hw_addr,argv[++i],6);
      if (verbose) printf("Ok\n");
      complete|=0x1000;
      };
   if (!strncasecmp(argv[i],"-protoid",8)){
      if (verbose) printf("Protocol Identifier: ");
      get_hex_value(pkt1.proto_id,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0800;
      };
   if (!strncasecmp(argv[i],"-protovid",9)){
      if (verbose) printf("Protocol Version Identifier: ");
      get_hex_value(&pkt1.proto_v_id,argv[++i],1);
      if (verbose) printf("Ok\n");
      complete|=0x0400;
      };
   if (!strncasecmp(argv[i],"-bpdu",5)){
      if (verbose) printf("BPDU Type: ");
      get_hex_value(&pkt1.bpdu_type,argv[++i],1);
      if (verbose) printf("Ok\n");
      complete|=0x0200;
      };
   if (!strncasecmp(argv[i],"-flags",6)){
      if (verbose) printf("Flags: ");
      get_hex_value(&pkt1.flags,argv[++i],1);
      if (verbose) printf("Ok\n");
      complete|=0x0100;
      };
   if (!strncasecmp(argv[i],"-rootid",7)){
      if (verbose) printf("Root Identifier: ");
      get_hex_value(pkt1.rootid,argv[++i],8);
      if (verbose) printf("Ok\n");
      complete|=0x0080;
      };
   if (!strncasecmp(argv[i],"-rootpc",7)){
      if (verbose) printf("Root Path Cost: ");
      get_hex_value(pkt1.rootpc,argv[++i],4);
      if (verbose) printf("Ok\n");
      complete|=0x0040;
      };
   if (!strncasecmp(argv[i],"-brid",5)){
      if (verbose) printf("Bridge Identifier: ");
      get_hex_value(pkt1.brid,argv[++i],8);
      if (verbose) printf("Ok\n");
      complete|=0x0020;
      };
   if (!strncasecmp(argv[i],"-portid",7)){
      if (verbose) printf("Port Identifier: ");
      get_hex_value(pkt1.portid,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0010;
      };
   if (!strncasecmp(argv[i],"-mage",5)){
      if (verbose) printf("Message Age: ");
      get_hex_value(pkt1.mage,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0008;
      };
   if (!strncasecmp(argv[i],"-maxage",7)){
      if (verbose) printf("Max Age: ");
      get_hex_value(pkt1.maxage,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0004;
      };
   if (!strncasecmp(argv[i],"-htime",6)){
      if (verbose) printf("Hello Time: ");
      get_hex_value(pkt1.hellotime,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0002;
      };
   if (!strncasecmp(argv[i],"-fdelay",7)){
      if (verbose) printf("Forward delay: ");
      get_hex_value(pkt1.fdelay,argv[++i],2);
      if (verbose) printf("Ok\n");
      complete|=0x0001;
      };
};

/* printf("complete: %04X\n",complete); DEBUG! */

/* Check, if all needed parameters set */
if ((complete & 0x0FFF) < 0x0FFF) fireexit(usage);

/* Set constant values */
pkt1.frame_type[0]=0x00;
pkt1.frame_type[1]=0x26;
pkt1.llc_dest=0x42;
pkt1.llc_src=0x42;
pkt1.unknown=03;
bzero(pkt1.padding,8);

if (verbose) {
    f=fopen("packet.dmp","a");
    fwrite(&pkt1,1,sizeof(pkt1),f);
    fclose(f);
    exit(0);
    };

/* Open raw socket */
if ((sd = socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))) < 0){
        perror("Can't get raw socket: ");
        exit(1);
        }

/* Get our hardware address, if not given */
if (!(complete & (12<<1))){
    strcpy(ifr.ifr_name, eth_dev);
    if (ioctl(sd, SIOCGIFHWADDR, &ifr) < 0){
	 perror("Can't get hardware address: ");
         close(sd);
         exit(1);
        };
    memcpy(pkt1.src_hw_addr,ifr.ifr_hwaddr.sa_data,6);
    };

/* Set device to use */
strcpy(saddr.sa_data,eth_dev);

/* Send prepared packet */
if(sendto(sd,&pkt1,sizeof(pkt1),0,&saddr,sizeof(saddr)) < 0)
   perror("Send packet");

/* Close socket */
close(sd);

exit(0);
}

             /*************** END MAIN PART ********************/

void fireexit(char* str){
fprintf(stderr,"%s\n",str);
exit(1);
}

void get_hex_value(char* buf,char* str,int len){

int i;
char c,val;

for(i=0;i<len;i++){
        if( !(c = tolower(*str++))) fireexit("Invalid hex value");
        if(isdigit(c)) val = c-'0';
        else if(c >= 'a' && c <= 'f') val = c-'a'+10;
        else fireexit("Invalid hex value");

        *buf = val << 4;
        if( !(c = tolower(*str++))) fireexit("Invalid hex value");
        if(isdigit(c)) val = c-'0';
        else if(c >= 'a' && c <= 'f') val = c-'a'+10;
        else fireexit("Invalid hex value");

        *buf++ |= val;

        if(*str == ':')str++;
        }
}
