On Fri, Dec 20, 2013 at 01:17:08PM +1000, David Gwynne wrote: > im glad you wrote a diff rather than simply complain that nat and tftp doesnt > work. the moving parts generally look good to me apart from the struct > src_addr and getopt chunks. > > please use sockaddr_storage instead of sockaddr in the src_addr struct. > > could you split the resolution of the argument to -a out into a separate > function and call it after the getopt loop, and pass the value of the family > variable to it too? > > you might want to use sockaddr_storage in unprivproc_pop too. > > cheers, > dlg >
done, I also nuked this from the man page, that was a brain-fart: +This has to be the IP to which an accompanying nat-to +.Xr pf 4 +rule translates outgoing packets. Additionally a pass out rule is generated, so it actually does work. ( I was wondering why the return traffic was passed and I thought it was the nat-to rule. Turns out I had a stall pass out rule from a previous tftp-proxy run where it created pass rules instead of rdr rules and then crashed and thus the anchor was never cleared. Since I never restarted the tftp client those rules still matched. ) Thanks, Florian diff --git tftp-proxy.8 tftp-proxy.8 index f0dc9a4..fb06ba04 100644 --- tftp-proxy.8 +++ tftp-proxy.8 @@ -34,6 +34,7 @@ .Sh SYNOPSIS .Nm tftp-proxy .Op Fl 46dv +.Op Fl a Ar address .Op Fl l Ar address .Op Fl p Ar port .Op Fl w Ar transwait @@ -51,7 +52,7 @@ a rule with divert-reply set. .Pp The proxy inserts .Xr pf 4 -pass rules using the +pass and / or rdr rules using the .Ar anchor facility to allow payload packets between the client and the server. Once the rules are inserted, @@ -76,6 +77,10 @@ to use IPv4 addresses only. Forces .Nm to use IPv6 addresses only. +.It Fl a Ar address +The proxy will use this as the source address for the initial request from +the client to the server for nat traversal. +Instead of a pass in rule a rdr rule will be generated. .It Fl d Do not daemonize. If this option is specified, diff --git tftp-proxy.c tftp-proxy.c index bcbecd7..7a85646 100644 --- tftp-proxy.c +++ tftp-proxy.c @@ -141,7 +141,7 @@ __dead void usage(void) { extern char *__progname; - fprintf(stderr, "usage: %s [-46dv] [-l address] [-p port]" + fprintf(stderr, "usage: %s [-46dv] [-a address] [-l address] [-p port]" " [-w transwait]\n", __progname); exit(1); } @@ -179,6 +179,15 @@ struct proxy_child { struct proxy_child *child = NULL; TAILQ_HEAD(, proxy_listener) proxy_listeners; +struct src_addr { + TAILQ_ENTRY(src_addr) entry; + struct sockaddr_storage addr; + socklen_t addrlen; +}; +TAILQ_HEAD(, src_addr) src_addrs; + +void source_addresses(const char*, int); + int main(int argc, char *argv[]) { @@ -190,12 +199,15 @@ main(int argc, char *argv[]) struct passwd *pw; char *addr = "localhost"; + char *saddr = NULL; char *port = "6969"; int family = AF_UNSPEC; int pair[2]; - while ((c = getopt(argc, argv, "46dvl:p:w:")) != -1) { + TAILQ_INIT(&src_addrs); + + while ((c = getopt(argc, argv, "46a:dvl:p:w:")) != -1) { switch (c) { case '4': family = AF_INET; @@ -203,6 +215,9 @@ main(int argc, char *argv[]) case '6': family = AF_INET6; break; + case 'a': + saddr = optarg; + break; case 'd': verbose = debug = 1; break; @@ -239,6 +254,9 @@ main(int argc, char *argv[]) if (pw == NULL) lerrx(1, "no %s user", NOPRIV_USER); + if (saddr != NULL) + source_addresses(saddr, family); + switch (fork()) { case -1: lerr(1, "fork"); @@ -312,6 +330,30 @@ main(int argc, char *argv[]) return(0); } +void +source_addresses(const char* name, int family) +{ + struct addrinfo hints, *res, *res0; + struct src_addr *saddr; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + hints.ai_flags = AI_PASSIVE; + error = getaddrinfo(name, "0", &hints, &res0); + if (error) + lerrx(1, "%s:%s: %s", name, "0", gai_strerror(error)); + for (res = res0; res != NULL; res = res->ai_next) { + if ((saddr = calloc(1, sizeof(struct src_addr))) == NULL) + lerrx(1, "calloc"); + memcpy(&(saddr->addr), res->ai_addr, + sizeof(struct sockaddr)); + saddr->addrlen = res->ai_addrlen; + TAILQ_INSERT_TAIL(&src_addrs, saddr, entry); + } + freeaddrinfo(res0); +} void proxy_privproc(int s, struct passwd *pw) @@ -360,7 +402,8 @@ privproc_pop(int fd, short events, void *arg) struct addr_pair req; struct privproc *p = arg; struct fd_reply *rep; - int add = 0; + struct src_addr *saddr; + int add = 0, found = 0; switch (evbuffer_read(p->buf, fd, sizeof(req))) { case 0: @@ -407,9 +450,24 @@ privproc_pop(int fd, short events, void *arg) &on, sizeof(on)) == -1) lerr(1, "privproc setsockopt(REUSEPORT)"); - if (bind(rep->fd, (struct sockaddr *)&req.src, - req.src.ss_len) == -1) - lerr(1, "privproc bind"); + if (TAILQ_EMPTY(&src_addrs)) { + if (bind(rep->fd, (struct sockaddr *)&req.src, + req.src.ss_len) == -1) + lerr(1, "privproc bind"); + } else { + TAILQ_FOREACH(saddr, &src_addrs, entry) + if (saddr->addr.ss_family == + req.src.ss_family) { + if (bind(rep->fd, (struct sockaddr*) + &saddr->addr, saddr->addrlen) == -1) + lerr(1, "privproc bind"); + found = 1; + break; + } + + if (found == 0) + lerr(1, "no source address found"); + } if (TAILQ_EMPTY(&p->replies)) add = 1; @@ -727,9 +785,13 @@ unprivproc_pop(int fd, short events, void *arg) } cmsgbuf; struct cmsghdr *cmsg; struct iovec iov; + struct sockaddr_storage saddr; + socklen_t len; int result; int s; + len = sizeof(struct sockaddr); + do { memset(&msg, 0, sizeof(msg)); iov.iov_base = &result; @@ -787,17 +849,34 @@ unprivproc_pop(int fd, short events, void *arg) if (prepare_commit(r->id) == -1) lerr(1, "%s: prepare_commit", __func__); - if (add_filter(r->id, PF_IN, (struct sockaddr *)&r->addrs.dst, - (struct sockaddr *)&r->addrs.src, - ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port), - IPPROTO_UDP) == -1) - lerr(1, "%s: couldn't add pass in", __func__); - - if (add_filter(r->id, PF_OUT, (struct sockaddr *)&r->addrs.dst, - (struct sockaddr *)&r->addrs.src, - ntohs(((struct sockaddr_in *)&r->addrs.src)->sin_port), - IPPROTO_UDP) == -1) - lerr(1, "%s: couldn't add pass out", __func__); + if (TAILQ_EMPTY(&src_addrs)) { + if (add_filter(r->id, PF_IN, (struct sockaddr *) + &r->addrs.dst, (struct sockaddr *)&r->addrs.src, + ntohs(((struct sockaddr_in *)&r->addrs.src) + ->sin_port), IPPROTO_UDP) == -1) + lerr(1, "%s: couldn't add pass in", __func__); + + if (add_filter(r->id, PF_OUT, (struct sockaddr *) + &r->addrs.dst, (struct sockaddr *)&r->addrs.src, + ntohs(((struct sockaddr_in *)&r->addrs.src) + ->sin_port), IPPROTO_UDP) == -1) + lerr(1, "%s: couldn't add pass out", __func__); + } else { + if (getsockname(s, (struct sockaddr*)&saddr, &len) == -1) + lerr(1, "%s: getsockname", __func__); + if (add_rdr(r->id, (struct sockaddr *)&r->addrs.dst, + (struct sockaddr*)&saddr, + ntohs(((struct sockaddr_in *)&saddr)->sin_port), + (struct sockaddr *)&r->addrs.src, + ntohs(((struct sockaddr_in *)&r->addrs.src)-> + sin_port), IPPROTO_UDP ) == -1) + lerr(1, "%s: couldn't add rdr rule", __func__); + if (add_filter(r->id, PF_OUT, (struct sockaddr *) + &r->addrs.dst, (struct sockaddr *)&r->addrs.src, + ntohs(((struct sockaddr_in *)&r->addrs.src) + ->sin_port), IPPROTO_UDP) == -1) + lerr(1, "%s: couldn't add pass out", __func__); + } if (do_commit() == -1) lerr(1, "%s: couldn't commit rules", __func__); -- I'm not entirely sure you are real.