/**
* Adds a route entry using RTNETLINK
*
* @author : Asanga Udugama (adu@comnets.uni-bremen.de)
* @date   : 19-jul-2005
*
*/

#include <bits/sockaddr.h>
#include <stdio.h>
#include <asm/types.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>


// gcc -o rt_add rt_add.c

struct {
	struct nlmsghdr		nlmsg_info;
	struct rtmsg		rtmsg_info;
	char			buffer[2048];
} netlink_req;

int fd;
struct sockaddr_nl local;
struct sockaddr_nl peer;   
struct msghdr msg_info;
struct iovec iov_info;
char read_buffer[8192];

struct nlmsghdr *nlmsg_ptr;
char *read_ptr;
int nlmsg_len;
struct rtmsg *rtmsg_ptr;
struct rtattr *rtattr_ptr;
int rtmsg_len;
char temp_buffer[8192];
int rtn;
int i;

char dst_str[128];
int prefix_val;
char gw_str[128];
int ifc_val;

int usage()
{
	printf("./rt_add address[/prefix] [gw gateway] dev number\n");
	printf("  e.g ./rt_add 192.168.2.224/29 gw 192.168.2.1 dev 2\n");
	printf("\n");
}

int parse_user_arguments(int argc, char *argv[])
{
	char *sptr;

	bzero(dst_str, 128);
	prefix_val = 0;
	bzero(gw_str, 128);
	ifc_val = 0;


	if( !(argc == 4 || argc == 6) ) {
		printf("Error in command line\n");
		usage();
		exit(1);
	}

	if((sptr = strstr(argv[1], "/")) != 0) {
		(*sptr) = '\0';
		sscanf(argv[1], "%s", dst_str);
		sptr++;
		sscanf(sptr, "%d", &prefix_val);	
	} else {
		sscanf(argv[1], "%s", dst_str);
		prefix_val = 32;
	}

	if(argc == 6) {
		sscanf(argv[3], "%s", gw_str);
		sscanf(argv[5], "%d", &ifc_val);
	} else {
		sscanf(argv[3], "%d", &ifc_val);
	}
}

/**
* Send a request to NETLINK to add the routing entry
*/
int send_route_add_request()
{
	bzero(&local, sizeof(local));
	local.nl_family = AF_NETLINK;
	local.nl_pad = 0;
	local.nl_pid = getpid();
	local.nl_groups = 0;
	if(bind(fd, (struct sockaddr*) &local, sizeof(local)) < 0) {
		printf("Error in sock bind\n");
		usage();
		exit(1);
	}


	bzero(&peer, sizeof(peer));
	peer.nl_family = AF_NETLINK;
	peer.nl_pad = 0;
	peer.nl_pid = 0;
	peer.nl_groups = 0;

	bzero(&msg_info, sizeof(msg_info));
	msg_info.msg_name = (void *) &peer;
	msg_info.msg_namelen = sizeof(peer);

	bzero(&netlink_req, sizeof(netlink_req));

	rtmsg_len = sizeof(struct rtmsg);
	
	// add destnation addr
	rtattr_ptr = (struct rtattr *) netlink_req.buffer;
	rtattr_ptr->rta_type = RTA_DST;
	rtattr_ptr->rta_len = sizeof(struct rtattr) + 4;
	inet_pton(AF_INET, dst_str, ((char *)rtattr_ptr) + sizeof(struct rtattr));
	rtmsg_len += rtattr_ptr->rta_len;

	if(strlen(gw_str) > 0) {
		// add gw addr
		rtattr_ptr = (struct rtattr *) (((char *)rtattr_ptr) 
							+ rtattr_ptr->rta_len);
		rtattr_ptr->rta_type = RTA_GATEWAY;
		rtattr_ptr->rta_len = sizeof(struct rtattr) + 4;
		inet_pton(AF_INET, gw_str, ((char *)rtattr_ptr) 
							+ sizeof(struct rtattr));
		rtmsg_len += rtattr_ptr->rta_len;
	}

	// add ifc index
	rtattr_ptr = (struct rtattr *) (((char *)rtattr_ptr) 
						+ rtattr_ptr->rta_len);
	rtattr_ptr->rta_type = RTA_OIF;
	rtattr_ptr->rta_len = sizeof(struct rtattr) + 4;
	memcpy(((char *)rtattr_ptr) + sizeof(struct rtattr), &ifc_val, 4); 

	rtmsg_len += rtattr_ptr->rta_len;

	//for(i = 0; i < (rtmsg_len - sizeof(struct rtmsg)); i++) { 
	//	printf("%02xx ", (unsigned char) netlink_req.buffer[i]);
	//}
	//printf("\n");

	netlink_req.nlmsg_info.nlmsg_len = NLMSG_LENGTH(rtmsg_len);
	netlink_req.nlmsg_info.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
	netlink_req.nlmsg_info.nlmsg_type = RTM_NEWROUTE;

	netlink_req.rtmsg_info.rtm_family = AF_INET;
	netlink_req.rtmsg_info.rtm_table = RT_TABLE_MAIN;
	netlink_req.rtmsg_info.rtm_dst_len = prefix_val;

	netlink_req.rtmsg_info.rtm_protocol = RTPROT_STATIC;
	netlink_req.rtmsg_info.rtm_scope = RT_SCOPE_UNIVERSE;
	netlink_req.rtmsg_info.rtm_type = RTN_UNICAST;

	iov_info.iov_base = (void *) &netlink_req.nlmsg_info;
	iov_info.iov_len = netlink_req.nlmsg_info.nlmsg_len;
	msg_info.msg_iov = &iov_info;
	msg_info.msg_iovlen = 1;

	rtn = sendmsg(fd, &msg_info, 0);
	if(rtn < 0) {
		printf("Error in sendmsg\n");
		usage();
		exit(1);
	}
}

int main(int argc, char *argv[])
{
	// open NETLINK socket
	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
	if(fd < 0) {
		printf("Error in sock open\n");
		usage();
		exit(1);
	}

	parse_user_arguments(argc, argv);
	send_route_add_request();

	close(fd);

	exit(0);
}
