* 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);
}

Reply via email to