NAT-PMP

Specification

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

#include <netinet/in.h>

#include <arpa/inet.h>

#include <net/if.h>
#include <net/route.h>

#include <err.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

static int find_default_gateway(struct sockaddr_in*, socklen_t);
static struct in_addr get_external_address(int);
static int forward_port(int, in_port_t, in_port_t);
static int destroy_port(int, in_port_t, in_port_t);

struct nat_externaddr {
	unsigned char  version;
	unsigned char  op;
	unsigned short result;
	unsigned int   seconds;
	struct in_addr ip4;
};

struct nat_portfwd_req {
	unsigned char  version;
	unsigned char  op;
	unsigned short reserved;
	in_port_t      i_port;
	in_port_t      e_port;
	unsigned int   lifetime;
};

struct nat_portfwd_reply {
	unsigned char  version;
	unsigned char  op;
	unsigned short result;
	unsigned int   timestamp;
	in_port_t      i_port;
	in_port_t      e_port;
	unsigned int   lifetime;
};

int
main()
{
	int fd;
	struct sockaddr_in addr;
	char buffer[512];

	bzero(buffer, sizeof(buffer));
	bzero(&addr, sizeof(addr));

	if (!find_default_gateway(&addr, sizeof addr))
		errx(1, "Unable to find default gateway");

	inet_ntop(AF_INET, &addr.sin_addr, buffer, sizeof(buffer));

	fprintf(stderr, "Default Gateway: %s\n", buffer);

	if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
		err(1, "socket");

	addr.sin_port = htons(5351);
	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1)
		err(1, "connect");

	addr.sin_addr = get_external_address(fd);

	inet_ntop(AF_INET, &addr.sin_addr, buffer, sizeof(buffer));

	fprintf(stderr, "External IP: %s\n", buffer);

	forward_port(fd, 22, 65022);

	close(fd);

	return 0;
}

int
destroy_port(int fd, in_port_t i_port, in_port_t e_port)
{
	struct nat_portfwd_req   req;
	struct nat_portfwd_reply reply;

	bzero(&req, sizeof req);

	req.version = 0;
	req.op = 2; /* TCP */
	req.i_port = htons(i_port);
	req.e_port = 0;
	req.lifetime = 0;

	send(fd, &req, sizeof(req), 0);
	usleep(250000);
	recv(fd, &reply, sizeof(reply), 0);

	fprintf(stderr, "Reply [version=%u, op=%u, result=%u, timestamp=%u, internal port=%u, external port=%u, lifetime=%u]\n",
			reply.version, ((unsigned int)reply.op - 128), reply.result, ntohl(reply.timestamp), ntohs(reply.i_port),
			ntohs(reply.e_port), ntohl(reply.lifetime));

	return (reply.result == 0);	
}

int
forward_port(int fd, in_port_t i_port, in_port_t e_port)
{
	struct nat_portfwd_req   req;
	struct nat_portfwd_reply reply;

	bzero(&req, sizeof req);

	req.version = 0;
	req.op = 2; /* TCP */
	req.i_port = htons(i_port);
	req.e_port = htons(e_port);
	req.lifetime = htonl(60);

	send(fd, &req, sizeof(req), 0);
	usleep(250000);
	recv(fd, &reply, sizeof(reply), 0);

	fprintf(stderr, "Reply [version=%u, op=%u, result=%u, timestamp=%u, internal port=%u, external port=%u, lifetime=%u]\n",
			reply.version, ((unsigned int)reply.op - 128), reply.result, ntohl(reply.timestamp), ntohs(reply.i_port),
			ntohs(reply.e_port), ntohl(reply.lifetime));

	return (reply.result == 0);	
}

struct in_addr
get_external_address(int fd)
{
	short req = 0;
	struct nat_externaddr reply;

	bzero(&reply, sizeof(reply));

	send(fd, &req, sizeof(req), 0);
	usleep(250000);
	recv(fd, &reply, sizeof(reply), 0);

	fprintf(stderr, "Reply [version=%u, op=%u, result=%u, seconds=%u, ip4=%u]\n", 
			reply.version, reply.op, reply.result , reply.seconds, reply.ip4.s_addr);
	return reply.ip4;
}



int
find_default_gateway(struct sockaddr_in* addr_out, socklen_t len)
{
	int fd;
	char buffer[512];
	struct rt_msghdr* hdr;
	struct sockaddr_in* addr;
	int retval = 0;

	if ((fd = socket(PF_ROUTE, SOCK_RAW, AF_INET)) == -1)
		err(1, "socket(PF_ROUTE)");

	hdr = (struct rt_msghdr*)&buffer[0];
	bzero(buffer, sizeof buffer);

	hdr->rtm_msglen = sizeof (struct rt_msghdr) + sizeof(struct sockaddr_in);
	hdr->rtm_type = RTM_GET;
	hdr->rtm_addrs = RTA_DST;
	hdr->rtm_version = RTM_VERSION;

	addr = (struct sockaddr_in*)&buffer[sizeof(struct rt_msghdr)];
	addr->sin_len = sizeof(struct sockaddr_in);
	addr->sin_addr.s_addr = INADDR_ANY;
	addr->sin_family = PF_INET;
	
	if (send(fd, buffer, hdr->rtm_msglen, 0) == -1)
		err(1, "send(fd)");

	if (recv(fd, buffer, sizeof buffer, 0) == -1)
		err(1, "recv(fd)");

	if (hdr->rtm_addrs & RTA_DST)
		addr++;

	if (hdr->rtm_addrs & RTA_GATEWAY) {
		memcpy(addr_out, addr, addr->sin_len);
		retval = 1;
	}
	
	close(fd);

	return retval;
}

Reminder to self: Forget the PITA, i.e. IGD is. :p

Leave a Reply

Your email address will not be published. Required fields are marked *

Anti-Spam Quiz: