* Stuart Henderson <s...@spacehopper.org> [2011-12-20 08:50:41]: > On 2011-12-18, Matt Dainty <m...@bodgit-n-scarper.com> wrote: > > ---8<--- DESCR ---8<--- > > natpmpd is a daemon that can be used on an OpenBSD NAT gateway to > > provide support for the NAT-PMP protocol on any internal networks which > > then allows a client to create and maintain rules in pf to map TCP and > > UDP connections to the external IP address on the NAT gateway to > > services running on the client itself. > > ---8<--- DESCR ---8<--- > > > > Tested on amd64, a test on something with strict alignment like sparc64 > > would be appreciated. > > Port looks good, I can try it on sparc64 if you have a simple way to test it..
Probably the easiest way is with the attached basic client. On the sparc64 host you can just use lo0 as the external interface so /etc/natpmpd.conf can contain: interface lo0 listen on ip.of.sparc64.host also with 'anchor "natpmpd"' somewhere in the pf ruleset. Then just run the client with "./a.out ip.of.sparc64.host". It will send a probe to get the external address of the host and then request a tcp mapping for port 8080 on the gateway to port 8080 on itself but the daemon will use a random high port. So you should get something like the following output: external NAT gateway address: 127.0.0.1 requested port 8080, got port 54715, lifetime 60 seconds Double-check the rule in the pf anchor has the same port and then after 60 seconds the mapping should expire and the rule will be removed from the anchor. Running the test again before the 60 seconds is up should return the same high port as the lease is effectively being renewed. Matt
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <err.h> #include <stdlib.h> #include <stdio.h> #include <netdb.h> #include <string.h> #include <unistd.h> #define NATPMP_MAX_PACKET_SIZE 16 struct natpmp_response { u_int8_t version; u_int8_t opcode; u_int16_t result; u_int32_t sssoe; union { struct { u_int32_t address; } announce; struct { u_int16_t port[2]; u_int32_t lifetime; } mapping; } data; }; int main(int argc, char *argv[]) { int s; int status; struct addrinfo hints; struct addrinfo *servinfo; unsigned char probe[2] = { 0, 0 }; /* Map TCP 8080 -> 8080 for 60 seconds */ unsigned char mapping[12] = { 0, 2, 0, 0, 0x1f, 0x90, 0x1f, 0x90, 0, 0, 0, 0x3c }; unsigned char storage[NATPMP_MAX_PACKET_SIZE]; ssize_t len; struct natpmp_response *r = (struct natpmp_response *)storage; u_int16_t result; struct in_addr address; if (argc < 2) errx(1, "Not enough arguments: [gateway address]"); memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_flags = 0; hints.ai_socktype = SOCK_DGRAM; if ((status = getaddrinfo(argv[1], "5351", &hints, &servinfo)) != 0 ) err(1, "getaddrinfo"); if ((s = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) err(1, "socket"); if (connect(s, servinfo->ai_addr, servinfo->ai_addrlen) == -1) err(1, "connect"); /* Probe */ len = 2; if ((len = send(s, probe, len, 0)) < 0) err(1, "send"); if ((len = read(s, (void *)storage, NATPMP_MAX_PACKET_SIZE)) < 0) err(1, "read"); if (len < 12) errx(1, "short probe reply"); if ((r->version != 0) || (r->opcode != 128)) errx(1, "bad probe reply"); if((result = ntohs(r->result)) != 0) errx(1, "bad result: %d", result); address.s_addr = r->data.announce.address; printf("external NAT gateway address: %s\n", inet_ntoa(address)); /* Create mapping */ len = 12; if ((len = send(s, mapping, len, 0)) < 0) err(1, "send"); if ((len = read(s, (void *)storage, NATPMP_MAX_PACKET_SIZE)) < 0) err(1, "read"); if (len < 16) errx(1, "short mapping reply"); if ((r->version != 0) || (r->opcode != 130)) errx(1, "bad mapping reply"); if ((result = ntohs(r->result)) != 0) errx(1, "bad result: %d", result); printf("requested port 8080, got port %d, lifetime %d seconds\n", ntohs(r->data.mapping.port[1]), ntohl(r->data.mapping.lifetime)); close(s); freeaddrinfo(servinfo); return (0); }