/*
	bgp.c

	Reference:
		RFC 1771: A Border Gateway Protocol 4 (BGP-4)

	BGP routers:
		206.24.210.61
*/

#include <stdio.h>
#include <fcntl.h>

#include <sys/time.h>
#include <sys/types.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <netdb.h>

#include <string.h>

/* BGP message types */
#define BGP_OPEN		1
#define BGP_UPDATE		2
#define BGP_NOTIFICATION	3
#define BGP_KEEPALIVE		4

/* BGP parameters */
#define BGP_PARM_AUTHENTICATE	1

struct bgp_header {
	unsigned char marker[16];	/* marker data */
	unsigned short length;		/* two bytes */
	unsigned char type;		/* one byte */
};

struct bgp_open_header {
	unsigned char version;	/* The current BGP version number is 4. */
	unsigned short my_as;	/* my Autonomous System */
	unsigned short hold_time;	/* proposed hold timer (secs) */
	unsigned long bgp_id;	/* determined on start-up */
	unsigned char opt_parm_len;	/* total length of the Optional Parameters field in octets. */	
};

struct bgp_notification_header {
	unsigned char error_code;
	unsigned char error_subcode;
	unsigned char data[6];
};

struct auth_info_t {
	unsigned char auth_code;
	unsigned char auth_data[252];
	unsigned short auth_data_len;
};

/* prototypes */
int bgp_send(unsigned char *payload, unsigned length, int type);
int bgp_open(unsigned short my_as, unsigned long bgp_id, struct auth_info_t *auth_info);
int decode_bgp_open(unsigned char *packet, unsigned short length);
int decode_bgp_notification(unsigned char *packet, unsigned short length);


FILE *fpo,*fpi;

int debug = 1;

unsigned char password[] = "password";

int main(int argc, char *argv[]) {
	int s, i;
	struct auth_info_t auth_info;

	struct sockaddr_in sa;
	struct hostent *hp;


	char *host = NULL;
	int port = 179;

	unsigned char buf[1024];
	struct bgp_header header;
	unsigned short bytes_in;

	if(argc!=2) {
		fprintf(stderr, "%s: must specify bgp router name/address.\n",
			argv[0]);
	} else host = argv[1];
    
	/* find the server */
	if(!(hp = gethostbyname(host))) {
		fprintf(stderr,"error: can't get host %s.\n",host);
		exit(1);
	}

  
	/* Setup the socket */
	memset(&sa, 0, sizeof(sa));  
	sa.sin_port = htons(port);
	memcpy((char *)&sa.sin_addr, (char *)hp->h_addr, hp->h_length);
	sa.sin_family = hp->h_addrtype;
  
	/* allocate the socket */
	if((s = socket(hp->h_addrtype, SOCK_STREAM, 0)) < 0) {
		fprintf(stderr,"error: can't get socket\n");
		exit(1);
	}
    
	/* connect to the server */
	if(connect(s, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
		close(s);
	        fprintf(stderr,"error: can't connect\n");
        	exit(1);
	}
    
	fpo = fdopen(s,"w");
	fpi = fdopen(s,"r");
    
	/* handle headers */

	auth_info.auth_code = 1;
	memcpy(auth_info.auth_data,password, strlen(password));
	auth_info.auth_data_len = strlen(password);
	
 	bgp_open(7012, 0xCFAB0001, NULL); /* my AS, bgp_id, auth data */
/*	bgp_open(100, 100, &auth_info); */

	while( !feof(fpi) ) {
		if( (bytes_in = fread(buf,1,19,fpi)) == 19 ) {
			memcpy(header.marker,buf,16);
			header.length = (buf[16] << 8) | buf[17];
			header.type = buf[18];

			if(debug >= 2) {
				printf("BGP HEADER RECEIVED:\n");
				for(i=0;i<16;i++) printf("%02x%c",header.marker[i],i<15?'.':' ');
				printf("length = %u, type = %u\n",
					header.length, header.type);
			}

			if( (bytes_in = fread(buf,1,header.length-19,fpi)) == (header.length-19) ) {
				switch(header.type) {
					case BGP_OPEN:
						decode_bgp_open(buf,header.length - 19);
						break;
					case BGP_NOTIFICATION:
						decode_bgp_notification(buf,header.length - 19);
						break;
					default:
						printf("UNKNOWN BGP MESSAGE TYPE: %u\n", header.type);
						for(i=0;i<bytes_in;i++) printf("%02x.",buf[i]);
						printf("\n");
				}
			} else printf("SHORT BGP READ: %d bytes\n", bytes_in);


		} else printf("SHORT BGP HEADER READ: %d bytes\n", bytes_in);
		
/*		fgets(buf,1024,fpi); */
	}

	while(!feof(fpi)) {
		i = fread(buf,1,1024,fpi);
	        if(i) fwrite(buf,1,i,stdout);
	        if(feof(fpi)) break;
	}
	
	close(s);
	exit(0);
}

int bgp_send(unsigned char *payload, unsigned length, int type) {
	struct bgp_header header;
	
	header.length = length + 19;
	header.type = type;
	memset(&header.marker,0xFF,16);

	fwrite(header.marker,1,16,fpo);
	fprintf(fpo, "%c%c", header.length>>8, header.length&0xFF);
	fwrite(&header.type,1,1,fpo);
	fwrite(payload,1,length,fpo);
	fflush(fpo);

	if(debug >= 2) printf("BGP MESSAGE SENT:\n"
		"length = %u (must be 19 to 4096), type = %u\n",
		header.length,
		header.type);

	return 19 + length;
}

int bgp_open(unsigned short my_as, unsigned long bgp_id, struct auth_info_t *auth_info) {
	struct bgp_open_header open_header;
	unsigned char open_message[19+255];
	unsigned short open_message_len;

	/* BGP OPEN message optional parameter data: */
	unsigned char parm_data[255];
	unsigned char parm_data_len;
	
	open_header.version = 4;
	open_header.my_as = my_as;
	open_header.hold_time = 60; /* seconds */
	open_header.bgp_id = bgp_id;
	open_header.opt_parm_len = 0;
	
	open_message[0] = open_header.version;
	open_message[1] = open_header.my_as >> 8;
	open_message[2] = open_header.my_as & 0xFF;

	open_message[3] = open_header.hold_time >> 8;
	open_message[4] = open_header.hold_time & 0xFF;

	open_message[5] = open_header.hold_time >> 24;
	open_message[6] = open_header.hold_time >> 16;
	open_message[7] = open_header.hold_time >> 8;
	open_message[8] = open_header.hold_time & 0xFF;;

	open_message[9] = open_header.opt_parm_len;
	
	open_message_len = 10;


	/* Authentication */
	if(auth_info) {
		/*	Build authentication parameter:
			[ParmType:1] [ParmLen:1] [ParmValue:variable]
		*/
		/* put the auth code into the parameter */
		parm_data[0] = auth_info->auth_code;
		parm_data_len = 1;
		/* Now add the authentication_data field to the parameter */
		memcpy(parm_data+1,auth_info->auth_data,auth_info->auth_data_len);
		parm_data_len += auth_info->auth_data_len;

		/* Add parameter to end of OPEN message header */
		open_message[open_message_len++] = BGP_PARM_AUTHENTICATE;
		open_message[open_message_len++] = parm_data_len;
		memcpy(open_message+open_message_len,parm_data,parm_data_len);
		open_message_len += parm_data_len;
	}
	
	bgp_send(open_message,open_message_len,BGP_OPEN);

	if(debug >= 1)
		printf("BGP OPEN MESSAGE SENT:\n"
			"\tmy_as = %u, hold_time = %u, bgp_id = %lu.%lu.%lu.%lu\n",
				open_header.my_as,
				open_header.hold_time,
				open_header.bgp_id>>24,
				open_header.bgp_id>>16 & 0xFF,
				open_header.bgp_id>>8  & 0xFF,
				open_header.bgp_id     & 0xFF);

	return open_message_len;
}

int decode_bgp_open(unsigned char *packet, unsigned short length) {
	struct bgp_open_header open_header;
	unsigned short p;
	
	open_header.version =   packet[0];
	open_header.my_as =     (packet[1] << 8) & packet[2];
	open_header.hold_time = (packet[4] << 8) & packet[3];
	open_header.bgp_id =    (packet[8] << 24) & (packet[7] << 16) &
				(packet[6] <<  8) & packet[5];
	open_header.opt_parm_len = packet[9];
	
	if(debug >= 1) printf("BGP OPEN MESSAGE RECEIVED:\n"
		"\tversion = %u, autonomous system = %u, hold_time = %u secs,\n"
		"\tbgp_id = %lu, opt_parm_len = %u bytes.\n",
			open_header.version,
			open_header.my_as,
			open_header.hold_time,
			open_header.bgp_id,
			open_header.opt_parm_len);
			
	for(p=0;p<open_header.opt_parm_len;p+=packet[10+p+1]) {
		printf("\toptional param:\n"
			"\ttype = %u\n"
			"\tlength = %u\n",
			packet[10+p], packet[10+p+1] );
			
	}

	return 0;
}

int decode_bgp_notification(unsigned char *packet, unsigned short length) {
	struct bgp_notification_header notify_header;
	int i;
	
	notify_header.error_code = packet[0];
	notify_header.error_subcode = packet[1];
	memcpy(notify_header.data,packet+2,6);
	
	if(debug >= 1) {
		printf("BGP NOTIFICATION MESSAGE RECEIVED:\n\t");
		if(debug >= 2) printf("error_code = %u, error_subcode = %u\n",
				notify_header.error_code,
				notify_header.error_subcode);

			switch(notify_header.error_code) {
				case 1:
					printf("Message Header Error: ");
					switch(notify_header.error_subcode) {
						case 1:
						printf("Connection Not Synchronized.\n");
						break;
					case 2:
						printf("Bad Message Length.\n");
						break;
					case 3:
						printf("Bad Message Type.\n");
						break;
					default:
						printf("UNKNOWN ERROR SUBCODE: %u\n",
							notify_header.error_subcode);
				}
				break;

				case 2:
					printf("OPEN Message Error: ");
					switch(notify_header.error_subcode) {
						case 1:		printf("Unsupported Version Number.\n");	break;
						case 2:		printf("Bad Peer AS.\n");			break;
						case 3:		printf("Bad BGP Identifier.\n");		break;
						case 4:		printf("Unsupported Optional Parameter.\n");	break;
						case 5:		printf("Authentication Failure.\n");		break;
						case 6:		printf("Unacceptable Hold Time.\n");		break;
						default:
							printf("UNKNOWN ERROR SUBCODE: %u\n",
								notify_header.error_subcode);
					}
					break;
				case 3:
					printf("UPDATE Message Error\n");
					break;
				case 4:
					printf("Hold Timer Expired\n");
					break;
				case 5:
					printf("Finite State Machine Error\n");
					break;
				case 6:
					printf("Cease\n");
					break;
				default:
					printf("UNKNOWN ERROR!\n");
			}
		printf("\n");
		if(length > 2) {
			printf("\tnotify data field: ");
			for(i=0;i<(length-2);i++) printf("%02x.",notify_header.data[i]);
			printf("\n");
		}
	} /* if(debug) */
			
	return 0;
}

/* 2232 McGee St., Berkeley, CA 94703 */

