// SPDX-License-Identifier: GPL-2.0-or-later
/* BGP dump to ascii converter
 * Copyright (C) 1999 Kunihiro Ishiguro
 */

#include <zebra.h>
#include <fcntl.h>

#include "zebra.h"
#include "stream.h"
#include "log.h"
#include "prefix.h"
#include "command.h"
#include "memory.h"
#include "privs.h"
#include "filter.h"

#include "bgpd/bgpd.h"
#include "bgpd/bgp_dump.h"
#include "bgpd/bgp_attr.h"
#include "bgpd/bgp_aspath.h"

/* privileges */
static zebra_capabilities_t _caps_p[] = {
	ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN,
};

struct zebra_privs_t bgpd_privs = {
#if defined(FRR_USER) && defined(FRR_GROUP)
	.user = FRR_USER,
	.group = FRR_GROUP,
#endif
#ifdef VTY_GROUP
	.vty_group = VTY_GROUP,
#endif
	.caps_p = _caps_p,
	.cap_num_p = array_size(_caps_p),
	.cap_num_i = 0,
};

enum MRT_MSG_TYPES {
	MSG_NULL,
	MSG_START,		  /* sender is starting up */
	MSG_DIE,		  /* receiver should shut down */
	MSG_I_AM_DEAD,		  /* sender is shutting down */
	MSG_PEER_DOWN,		  /* sender's peer is down */
	MSG_PROTOCOL_BGP,	 /* msg is a BGP packet */
	MSG_PROTOCOL_RIP,	 /* msg is a RIP packet */
	MSG_PROTOCOL_IDRP,	/* msg is an IDRP packet */
	MSG_PROTOCOL_RIPNG,       /* msg is a RIPNG packet */
	MSG_PROTOCOL_BGP4PLUS,    /* msg is a BGP4+ packet */
	MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
	MSG_PROTOCOL_OSPF,	/* msg is an OSPF packet */
	MSG_TABLE_DUMP		  /* routing table dump */
};

static void attr_parse(struct stream *s, uint16_t len)
{
	unsigned int flag;
	unsigned int type;
	uint16_t length;
	uint16_t lim;

	lim = s->getp + len;

	printf("%s s->getp %zd, len %d, lim %d\n", __func__, s->getp, len, lim);

	while (s->getp < lim) {
		flag = stream_getc(s);
		type = stream_getc(s);

		if (CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN))
			length = stream_getw(s);
		else
			length = stream_getc(s);

		printf("FLAG: %d\n", flag);
		printf("TYPE: %d\n", type);
		printf("Len: %d\n", length);

		switch (type) {
		case BGP_ATTR_ORIGIN: {
			uint8_t origin;
			origin = stream_getc(s);
			printf("ORIGIN: %d\n", origin);
		} break;
		case BGP_ATTR_AS_PATH: {
			struct aspath *aspath;

			aspath = aspath_parse(s, length, 1,
					      bgp_get_asnotation(NULL));
			printf("ASPATH: %s\n", aspath->str);
			aspath_free(aspath);
		} break;
		case BGP_ATTR_NEXT_HOP: {
			struct in_addr nexthop;
			nexthop.s_addr = stream_get_ipv4(s);
			printf("NEXTHOP: %pI4\n", &nexthop);
		} break;
		default:
			stream_getw_from(s, length);
			break;
		}
	}
}

int main(int argc, char **argv)
{
	int ret;
	int fd;
	struct stream *s;
	time_t now;
	int type;
	int subtype;
	size_t len;
	int source_as;
	int dest_as;
	ifindex_t ifindex;
	int family;
	struct in_addr sip;
	struct in_addr dip;
	uint16_t viewno, seq_num;
	struct prefix_ipv4 p;
	char tbuf[32];

	s = stream_new(10000);

	if (argc != 2) {
		fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
		exit(1);
	}
	fd = open(argv[1], O_RDONLY);
	if (fd < 0) {
		fprintf(stdout,
			"%% Can't open configuration file %s due to '%s'.\n",
			argv[1], safe_strerror(errno));
		exit(1);
	}

	while (1) {
		stream_reset(s);

		ret = stream_read(s, fd, 12);
		if (ret != 12) {
			if (!ret)
				printf("END OF FILE\n");
			else if (ret < 0)
				printf("ERROR OF READ\n");
			else
				printf("UNDERFLOW\n");
			break;
		}

		/* Extract header. */
		now = stream_getl(s);
		type = stream_getw(s);
		subtype = stream_getw(s);
		len = stream_getl(s);

		printf("TIME: %s", ctime_r(&now, tbuf));

		/* printf ("TYPE: %d/%d\n", type, subtype); */

		if (type == MSG_PROTOCOL_BGP4MP)
			printf("TYPE: BGP4MP");
		else if (type == MSG_PROTOCOL_BGP4MP_ET)
			printf("TYPE: BGP4MP_ET");
		else if (type == MSG_TABLE_DUMP)
			printf("TYPE: MSG_TABLE_DUMP");
		else
			printf("TYPE: Unknown %d", type);

		if (type == MSG_TABLE_DUMP)
			switch (subtype) {
			case AFI_IP:
				printf("/AFI_IP\n");
				break;
			case AFI_IP6:
				printf("/AFI_IP6\n");
				break;
			default:
				printf("/UNKNOWN %d", subtype);
				break;
			}
		else {
			switch (subtype) {
			case BGP4MP_STATE_CHANGE:
				printf("/CHANGE\n");
				break;
			case BGP4MP_MESSAGE:
				printf("/MESSAGE\n");
				break;
			case BGP4MP_ENTRY:
				printf("/ENTRY\n");
				break;
			case BGP4MP_SNAPSHOT:
				printf("/SNAPSHOT\n");
				break;
			default:
				printf("/UNKNOWN %d", subtype);
				break;
			}
		}

		printf("len: %zd\n", len);

		ret = stream_read(s, fd, len);
		if (ret != (int)len) {
			if (!ret)
				printf("END OF FILE 2\n");
			else if (ret < 0)
				printf("ERROR OF READ 2\n");
			else
				printf("UNDERFLOW 2\n");
			break;
		}

		/* printf ("now read %d\n", len); */

		if (type == MSG_TABLE_DUMP) {
			uint8_t status;
			time_t originated;
			struct in_addr peer;
			uint16_t attrlen;

			viewno = stream_getw(s);
			seq_num = stream_getw(s);
			printf("VIEW: %d\n", viewno);
			printf("SEQUENCE: %d\n", seq_num);

			/* start */
			while (s->getp < len - 16) {
				p.prefix.s_addr = stream_get_ipv4(s);
				p.prefixlen = stream_getc(s);
				printf("PREFIX: %pI4/%d\n", &p.prefix,
				       p.prefixlen);

				status = stream_getc(s);
				originated = stream_getl(s);
				peer.s_addr = stream_get_ipv4(s);
				source_as = stream_getw(s);

				printf("FROM: %pI4 AS%d\n", &peer, source_as);
				printf("ORIGINATED: %s", ctime_r(&originated,
								 tbuf));

				attrlen = stream_getw(s);
				printf("ATTRLEN: %d\n", attrlen);

				attr_parse(s, attrlen);

				printf("STATUS: 0x%x\n", status);
			}
		} else {
			source_as = stream_getw(s);
			dest_as = stream_getw(s);
			printf("source_as: %d\n", source_as);
			printf("dest_as: %d\n", dest_as);

			ifindex = stream_getw(s);
			family = stream_getw(s);

			printf("ifindex: %d\n", ifindex);
			printf("family: %d\n", family);

			sip.s_addr = stream_get_ipv4(s);
			dip.s_addr = stream_get_ipv4(s);

			printf("saddr: %pI4\n", &sip);
			printf("daddr: %pI4\n", &dip);

			printf("\n");
		}
	}
	close(fd);
	return 0;
}
