is it possible (practical) to add af-to

Curtis Villamizar curtis at ipv6.occnc.com
Fri Sep 19 00:26:38 UTC 2014


Hi,

NAT46 and NAT64 require af-to or equivalent.

This may be naive on my part but it doesn't seem like it would be a
lot of trouble to add af-to to the existing pf.

That said, I am aware of the discussion of further diverging from the
openbsd pf as discussed in the thread in the archives at
http://lists.freebsd.org/pipermail/freebsd-pf/2014-July/007391.html

I'm willing to contribute code if there is a chance anyone would be
willing to take it as an interim until freebsd pf catches up to
openbsd pf.

Thoughts on this below.

Curtis



Currently the syntax of rdr and nat accept the following:

  nat-rule   = [ "no" ] "nat" [ "pass" [ "log" [ "(" logopts ")" ] ] ]
               [ "on" ifspec ] [ af ]
               [ protospec ] hosts [ "tag" string ] [ "tagged" string ]
               [ "->" ( redirhost | "{" redirhost-list "}" )
               [ portspec ] [ pooltype ] [ "static-port" ] ]

  rdr-rule   = [ "no" ] "rdr" [ "pass" [ "log" [ "(" logopts ")" ] ] ]
               [ "on" ifspec ] [ af ]
               [ protospec ] hosts [ "tag" string ] [ "tagged" string ]
               [ "->" ( redirhost | "{" redirhost-list "}" )
               [ portspec ] [ pooltype ] ]

Both of these end in:

               [ "->" ( redirhost | "{" redirhost-list "}" )
               [ portspec ] [ pooltype ] ]

(with nat-rule having the optional [ "static-port" ]).

Changing the syntax is no big deal and that would be the easy part.

In sbin/pf/parse.y some change would be needed to the redirpool rule
where the two forms are (yacc actions omitted):

redirpool	:
		| ARROW redirspec
		| ARROW redirspec PORT portstar

to this (or to redirspec) we'd have to add an af_to which would be
similar to the af rule but with the keywork af-to:

af_to	; /* empty */   { $$ = 0; }
	| AF_TO INET	{ $$ = AF_INET; }
	| AF_TO INET6	{ $$ = AF_INET6; }

If this is added to the redirpool then af_to would appear after ARROW
and $2 and $4 are bumped.  It means adding sa_family_t af to the
struct redirection.  

In the natrule rule if ((! $9->af) || (r.af == $9->af)), then nothing
needs to be done, else an address family translation is needed.  The
following lines have to be changed, replacing r.af with $9->af adding
a line before this to replace 0 with r.af if af_to was empty.

	remove_invalid_hosts(&$9->host, &r.af);
	if (invalid_redirect($9->host, r.af))
		YYERROR;
	if (check_netmask($9->host, r.af))
		YYERROR;

At this point the rule can be one address family and all of the
redirect addresses consistent with each other but not necissarily the
same af as the rule.  The next problem is that there is no way to pass
this af to expand_rule.  That's OK since $9->host ends up as the
protos argument to expand_rule and the af is in each entry.  On this
case the af in proto won't match the af in src_hosts and the
consistency checks (nat_consistent, rdr_consistent) don't check and
its off to pfctl_add_rule.

This gets us to a laer ioctl call of type DIOCADDRULE which puts us in
the kernel code in pf_ioctl.c.

In the case DIOCADDRULE code the checks of pr->rule.af are no longer
enough.  Similar checks are needed on the rule actions.  Something
along the lines of:

#ifndef INET
	if (pr->rule.af == AF_INET) {
		error = EAFNOSUPPORT;
		break;
	}
	if ((pr->rule.action == PF_NAT)
	    || (pr->rule.action == PF_RDR)) {
		if (rule_dst_has_af(pr->rule.dst.addr), AF_INET) {
			error = EAFNOSUPPORT;
			break:
		}
	}
#endif /* INET */

Where rule_dst_has_af loops through the table and checks for
instances of the af in the argument list.

Where the real work would have to be done is in pf_test, pf_test_rule,
pf_get_translation, pf_match_translation, and pf_create_state, and
some functions they use, such as pf_map_addr where mixed af is not
anticipated.  This would definitely be the messy part of any attempt
to make af-to work.

There is no INET/INET6 specific code in pf_test_rule except for ICMP
there is no INET/INET6 specific code and getting the packet length
when dropping TCP and sending a RST.  As a first cut, if ICMP from/to
ICMP6 in NAT and RDR didn't work that would be OK.  A FAIL would send
back TCP RST using the source af, so that code should be OK.

My understanding of this code is still very fuzzy after about a half
day of looking at it.  Perhaps looking at the openbsd pf would help
(hopefully they still resemble each other).

Any help would be appreciated, but for now I'd just like to know
whether if I took the trouble to add af-to support the code would be
picked up (after code review, testing, etc).  I haven't yet decided
whether this is something I want to take on in the first place.


More information about the freebsd-pf mailing list