change route-to so it sends packets to IPs instead of interfaces.
authordlg <dlg@openbsd.org>
Mon, 1 Feb 2021 00:31:04 +0000 (00:31 +0000)
committerdlg <dlg@openbsd.org>
Mon, 1 Feb 2021 00:31:04 +0000 (00:31 +0000)
this is a significant (and breaking) reworking of the policy based
routing that pf can do. the intention is to make it as easy as
nat/rdr to use, and more robust when it's operating.

the main reasons for this change are:

- route-to, reply-to, and dup-to do not work with pfsync

 this is because the information about where to route-to is stored in
 rules, and it is hard to have a ruleset synced between firewalls,
 and impossible to have them synced 100% of the time.

- i can make my boxes panic in certain situations using route-to

 yeah...

- the configuration and syntax for route-to rules are confusing.

 the argument to route-to and co is an interace name with an optional
 ip address. there are several problems with this. one is that people
 tend to think about routing as sending packets to peers by their
 address, not by the interface they're reachable on. another is that
 we currently have no way to synchronise interface topology information
 between firewalls, so using an interface to say where packets go
 means we can't do failover of these states with pfsync. another
 is that a change in routing topology means a host may become
 reachable over a different interface. tying routing policy to
 interfaces gets in the way of failover and load balancing.

this change does the following:

- stores the route info in the state instead of the pf rule

 this allows route-to to keep working when the ruleset changes, and
 allows route-to info to be sent over pfsync. there's enough spare bits
 in pfsync messages that the protocol doesnt break.

 the caveat is that route-to becomes tied to pass rules that create
 state, like rdr-to and nat-to.

- the argument to route-to etc is a destination ip address

 it's not limited to a next-hop address (thought a next-hop can be a
 destination address). this allows for the failover and load balancing
 referred to above.

- deprecates the address@interface host syntax in pfctl

 because routing is done entirely by IPs, the interface is derived from
 the route lookup, not pf. any attempt to use the @interface syntax
 will fail now in all contexts.

there's enthusiasm from proctor@ jmatthew@ and others
ok sashan@ bluhm@

sbin/pfctl/parse.y
sbin/pfctl/pfctl_parser.c
share/man/man5/pf.conf.5
sys/net/if_pfsync.c
sys/net/pf.c
sys/net/pfvar.h

index 91cc7aa..a10bc6f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: parse.y,v 1.708 2021/01/12 00:10:34 bluhm Exp $       */
+/*     $OpenBSD: parse.y,v 1.709 2021/02/01 00:31:04 dlg Exp $ */
 
 /*
  * Copyright (c) 2001 Markus Friedl.  All rights reserved.
@@ -276,6 +276,7 @@ struct filter_opts {
        struct redirspec         nat;
        struct redirspec         rdr;
        struct redirspec         rroute;
+       u_int8_t                 rt;
 
        /* scrub opts */
        int                      nodf;
@@ -284,15 +285,6 @@ struct filter_opts {
        int                      randomid;
        int                      max_mss;
 
-       /* route opts */
-       struct {
-               struct node_host        *host;
-               u_int8_t                 rt;
-               u_int8_t                 pool_opts;
-               sa_family_t              af;
-               struct pf_poolhashkey   *key;
-       }                        route;
-
        struct {
                u_int32_t       limit;
                u_int32_t       seconds;
@@ -372,7 +364,7 @@ void                 expand_label(char *, size_t, const char *, u_int8_t,
                    struct node_port *, u_int8_t);
 int             expand_divertspec(struct pf_rule *, struct divertspec *);
 int             collapse_redirspec(struct pf_pool *, struct pf_rule *,
-                   struct redirspec *rs, u_int8_t);
+                   struct redirspec *rs, int);
 int             apply_redirspec(struct pf_pool *, struct pf_rule *,
                    struct redirspec *, int, struct node_port *);
 void            expand_rule(struct pf_rule *, int, struct node_if *,
@@ -518,7 +510,6 @@ int parseport(char *, struct range *r, int);
 %type  <v.host>                ipspec xhost host dynaddr host_list
 %type  <v.host>                table_host_list tablespec
 %type  <v.host>                redir_host_list redirspec
-%type  <v.host>                route_host route_host_list routespec
 %type  <v.os>                  os xos os_list
 %type  <v.port>                portspec port_list port_item
 %type  <v.uid>                 uids uid_list uid_item
@@ -975,7 +966,7 @@ anchorrule  : ANCHOR anchorname dir quick interface af proto fromto
                                YYERROR;
                        }
 
-                       if ($9.route.rt) {
+                       if ($9.rt) {
                                yyerror("cannot specify route handling "
                                    "on anchors");
                                YYERROR;
@@ -1843,37 +1834,13 @@ pfrule          : action dir logquick interface af proto fromto
                        decide_address_family($7.src.host, &r.af);
                        decide_address_family($7.dst.host, &r.af);
 
-                       if ($8.route.rt) {
-                               if (!r.direction) {
+                       if ($8.rt) {
+                               if ($8.rt != PF_DUPTO && !r.direction) {
                                        yyerror("direction must be explicit "
                                            "with rules that specify routing");
                                        YYERROR;
                                }
-                               r.rt = $8.route.rt;
-                               r.route.opts = $8.route.pool_opts;
-                               if ($8.route.key != NULL)
-                                       memcpy(&r.route.key, $8.route.key,
-                                           sizeof(struct pf_poolhashkey));
-                       }
-                       if (r.rt) {
-                               decide_address_family($8.route.host, &r.af);
-                               if ((r.route.opts & PF_POOL_TYPEMASK) ==
-                                   PF_POOL_NONE && ($8.route.host->next != NULL ||
-                                   $8.route.host->addr.type == PF_ADDR_TABLE ||
-                                   DYNIF_MULTIADDR($8.route.host->addr)))
-                                       r.route.opts |= PF_POOL_ROUNDROBIN;
-                               if ($8.route.host->next != NULL) {
-                                       if (!PF_POOL_DYNTYPE(r.route.opts)) {
-                                               yyerror("address pool option "
-                                                   "not supported by type");
-                                               YYERROR;
-                                       }
-                               }
-                               /* fake redirspec */
-                               if (($8.rroute.rdr = calloc(1,
-                                   sizeof(*$8.rroute.rdr))) == NULL)
-                                       err(1, "$8.rroute.rdr");
-                               $8.rroute.rdr->host = $8.route.host;
+                               r.rt = $8.rt;
                        }
 
                        if (expand_divertspec(&r, &$8.divert))
@@ -2137,30 +2104,14 @@ filter_opt      : USER uids {
                            sizeof(filter_opts.nat.pool_opts));
                        filter_opts.nat.pool_opts.staticport = 1;
                }
-               | ROUTETO routespec pool_opts {
-                       filter_opts.route.host = $2;
-                       filter_opts.route.rt = PF_ROUTETO;
-                       filter_opts.route.pool_opts = $3.type | $3.opts;
-                       memcpy(&filter_opts.rroute.pool_opts, &$3,
-                           sizeof(filter_opts.rroute.pool_opts));
-                       if ($3.key != NULL)
-                               filter_opts.route.key = $3.key;
-               }
-               | REPLYTO routespec pool_opts {
-                       filter_opts.route.host = $2;
-                       filter_opts.route.rt = PF_REPLYTO;
-                       filter_opts.route.pool_opts = $3.type | $3.opts;
-                       if ($3.key != NULL)
-                               filter_opts.route.key = $3.key;
-               }
-               | DUPTO routespec pool_opts {
-                       filter_opts.route.host = $2;
-                       filter_opts.route.rt = PF_DUPTO;
-                       filter_opts.route.pool_opts = $3.type | $3.opts;
-                       memcpy(&filter_opts.rroute.pool_opts, &$3,
-                           sizeof(filter_opts.rroute.pool_opts));
-                       if ($3.key != NULL)
-                               filter_opts.route.key = $3.key;
+               | ROUTETO routespec {
+                       filter_opts.rt = PF_ROUTETO;
+               }
+               | REPLYTO routespec {
+                       filter_opts.rt = PF_REPLYTO;
+               }
+               | DUPTO routespec {
+                       filter_opts.rt = PF_DUPTO;
                }
                | not RECEIVEDON if_item {
                        if (filter_opts.rcv) {
@@ -3743,122 +3694,21 @@ pool_opt       : BITMASK       {
                }
                ;
 
-route_host     : STRING                        {
-                       /* try to find @if0 address specs */
-                       if (strrchr($1, '@') != NULL) {
-                               if (($$ = host($1, pf->opts)) == NULL)  {
-                                       yyerror("invalid host for route spec");
-                                       YYERROR;
-                               }
-                               free($1);
-                       } else {
-                               $$ = calloc(1, sizeof(struct node_host));
-                               if ($$ == NULL)
-                                       err(1, "route_host: calloc");
-                               $$->ifname = $1;
-                               $$->addr.type = PF_ADDR_NONE;
-                               set_ipmask($$, 128);
-                               $$->next = NULL;
-                               $$->tail = $$;
-                       }
-               }
-               | STRING '/' STRING             {
-                       char    *buf;
-
-                       if (asprintf(&buf, "%s/%s", $1, $3) == -1)
-                               err(1, "host: asprintf");
-                       free($1);
-                       if (($$ = host(buf, pf->opts)) == NULL) {
-                               /* error. "any" is handled elsewhere */
-                               free(buf);
-                               yyerror("could not parse host specification");
+routespec      : redirspec pool_opts {
+                       struct redirection *redir;
+                       if (filter_opts.rt != PF_NOPFROUTE) {
+                               yyerror("cannot respecify "
+                                   "route-to/reply-to/dup-to");
                                YYERROR;
                        }
-                       free(buf);
-               }
-               | '<' STRING '>'        {
-                       if (strlen($2) >= PF_TABLE_NAME_SIZE) {
-                               yyerror("table name '%s' too long", $2);
-                               free($2);
-                               YYERROR;
-                       }
-                       $$ = calloc(1, sizeof(struct node_host));
-                       if ($$ == NULL)
-                               err(1, "host: calloc");
-                       $$->addr.type = PF_ADDR_TABLE;
-                       if (strlcpy($$->addr.v.tblname, $2,
-                           sizeof($$->addr.v.tblname)) >=
-                           sizeof($$->addr.v.tblname))
-                               errx(1, "host: strlcpy");
-                       free($2);
-                       $$->next = NULL;
-                       $$->tail = $$;
-               }
-               | dynaddr '/' NUMBER            {
-                       struct node_host        *n;
-
-                       if ($3 < 0 || $3 > 128) {
-                               yyerror("bit number too big");
-                               YYERROR;
-                       }
-                       $$ = $1;
-                       for (n = $1; n != NULL; n = n->next)
-                               set_ipmask(n, $3);
-               }
-               | '(' STRING host ')'           {
-                       struct node_host        *n;
-
-                       $$ = $3;
-                       /* XXX check masks, only full mask should be allowed */
-                       for (n = $3; n != NULL; n = n->next) {
-                               if ($$->ifname) {
-                                       yyerror("cannot specify interface twice "
-                                           "in route spec");
-                                       YYERROR;
-                               }
-                               if (($$->ifname = strdup($2)) == NULL)
-                                       errx(1, "host: strdup");
-                       }
-                       free($2);
-               }
-               ;
-
-route_host_list        : route_host optweight optnl            { 
-                       if ($2 > 0) {
-                               struct node_host        *n;
-                               for (n = $1; n != NULL; n = n->next)
-                                       n->weight = $2;
-                       }
-                       $$ = $1;
-               }
-               | route_host_list comma route_host optweight optnl {
-                       if ($1->af == 0)
-                               $1->af = $3->af;
-                       if ($1->af != $3->af) {
-                               yyerror("all pool addresses must be in the "
-                                   "same address family");
-                               YYERROR;
-                       }
-                       $1->tail->next = $3;
-                       $1->tail = $3->tail;
-                       if ($4 > 0) {
-                               struct node_host        *n;
-                               for (n = $3; n != NULL; n = n->next)
-                                       n->weight = $4;
-                       }
-                       $$ = $1;
-               }
-               ;
-
-routespec      : route_host optweight                  {
-                       if ($2 > 0) {
-                               struct node_host        *n;
-                               for (n = $1; n != NULL; n = n->next)
-                                       n->weight = $2;
-                       }
-                       $$ = $1;
+                       redir = calloc(1, sizeof(*redir));
+                       if (redir == NULL)
+                               err(1, "routespec calloc");
+                       redir->host = $1;
+                       filter_opts.rroute.rdr = redir;
+                       memcpy(&filter_opts.rroute.pool_opts, &$2,
+                           sizeof(filter_opts.rroute.pool_opts));
                }
-               | '{' optnl route_host_list '}' { $$ = $3; }
                ;
 
 timeout_spec   : STRING NUMBER
@@ -4058,6 +3908,10 @@ rule_consistent(struct pf_rule *r)
                yyerror("keep state is great, but only for pass rules");
                problems++;
        }
+       if (r->rt && !r->keep_state) {
+               yyerror("route-to, reply-to and dup-to require keep state");
+               problems++;
+       }
        if (r->rule_flag & PFRULE_STATESLOPPY &&
            (r->keep_state == PF_STATE_MODULATE ||
            r->keep_state == PF_STATE_SYNPROXY)) {
@@ -4478,7 +4332,7 @@ expand_divertspec(struct pf_rule *r, struct divertspec *ds)
 
 int
 collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
-    struct redirspec *rs, u_int8_t allow_if)
+    struct redirspec *rs, int routing)
 {
        struct pf_opt_tbl *tbl = NULL;
        struct node_host *h, *hprev = NULL;
@@ -4494,6 +4348,15 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                r->naf = rs->af;
 
        for (h = rs->rdr->host; h != NULL; h = h->next) {
+               if (routing) {
+                       if (h->addr.type == PF_ADDR_DYNIFTL &&
+                           h->addr.iflags != PFI_AFLAG_PEER) {
+                               yyerror("route spec requires :peer with "
+                                   "dynamic interface addresses");
+                               return (1);
+                       }
+               }
+
                /* set rule address family if redirect spec has one */
                if (rs->af && !r->af && !af) {
                        /* swap address families for af-to */
@@ -4515,7 +4378,7 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                        if (!r->af && af && af != h->af) {
                                yyerror("%s spec contains addresses with "
                                    "different address families",
-                                   allow_if ? "routing" : "translation");
+                                   routing ? "routing" : "translation");
                                return (1);
                        }
                } else if (h->af) {     /* af-to case */
@@ -4526,7 +4389,7 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                        if (rs->af && rs->af != h->af) {
                                yyerror("%s spec contains addresses that "
                                    "don't match target address family",
-                                   allow_if ? "routing" : "translation");
+                                   routing ? "routing" : "translation");
                                return (1);
                        }
                }
@@ -4541,8 +4404,9 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
 
                if (naddr == 0) {       /* the first host */
                        rpool->addr = h->addr;
-                       if (!allow_if && h->ifname) {
-                               yyerror("@if not permitted for translation");
+                       if (h->ifname) {
+                               yyerror("@if not permitted for %s",
+                                   routing ? "routing" : "translation");
                                return (1);
                        }
                        if (h->ifname && strlcpy(rpool->ifname, h->ifname,
@@ -4564,8 +4428,9 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                                    "not supported for translation or routing");
                                return (1);
                        }
-                       if (!allow_if && h->ifname) {
-                               yyerror("@if not permitted for translation");
+                       if (h->ifname) {
+                               yyerror("@if not permitted for %s",
+                                   routing ? "routing" : "translation");
                                return (1);
                        }
                        if (hprev) {
@@ -4596,7 +4461,7 @@ collapse_redirspec(struct pf_pool *rpool, struct pf_rule *r,
                r->af = af;
        if (!naddr) {
                yyerror("af mismatch in %s spec",
-                   allow_if ? "routing" : "translation");
+                   routing ? "routing" : "translation");
                return (1);
        }
        if (tbl) {
@@ -5992,7 +5857,7 @@ filteropts_to_rule(struct pf_rule *r, struct filter_opts *opts)
                yyerror("af-to can only be used with direction in");
                return (1);
        }
-       if ((opts->marker & FOM_AFTO) && opts->route.rt) {
+       if ((opts->marker & FOM_AFTO) && opts->rt) {
                yyerror("af-to cannot be used together with "
                    "route-to, reply-to, dup-to");
                return (1);
index 4636c14..6f39ad7 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfctl_parser.c,v 1.345 2021/01/12 00:10:34 bluhm Exp $ */
+/*     $OpenBSD: pfctl_parser.c,v 1.346 2021/02/01 00:31:04 dlg Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -1615,17 +1615,12 @@ host(const char *s, int opts)
 {
        struct node_host        *h = NULL, *n;
        int                      mask = -1;
-       char                    *p, *ps, *if_name;
+       char                    *p, *ps;
        const char              *errstr;
 
        if ((ps = strdup(s)) == NULL)
                err(1, "%s: strdup", __func__);
 
-       if ((if_name = strrchr(ps, '@')) != NULL) {
-               if_name[0] = '\0';
-               if_name++;
-       }
-
        if ((p = strchr(ps, '/')) != NULL) {
                mask = strtonum(p+1, 0, 128, &errstr);
                if (errstr) {
@@ -1642,10 +1637,6 @@ host(const char *s, int opts)
                goto error;
        }
 
-       if (if_name && if_name[0])
-               for (n = h; n != NULL; n = n->next)
-                       if ((n->ifname = strdup(if_name)) == NULL)
-                               err(1, "%s: strdup", __func__);
        for (n = h; n != NULL; n = n->next) {
                n->addr.type = PF_ADDR_ADDRMASK;
                n->weight = 0;
index 5be2f03..3806dbc 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: pf.conf.5,v 1.585 2020/12/07 08:29:41 sashan Exp $
+.\"    $OpenBSD: pf.conf.5,v 1.586 2021/02/01 00:31:04 dlg Exp $
 .\"
 .\" Copyright (c) 2002, Daniel Hartmeier
 .\" Copyright (c) 2003 - 2013 Henning Brauer <henning@openbsd.org>
@@ -28,7 +28,7 @@
 .\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 .\" POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: December 7 2020 $
+.Dd $Mdocdate: February 1 2021 $
 .Dt PF.CONF 5
 .Os
 .Sh NAME
@@ -1113,8 +1113,8 @@ the incoming connection arrived through (symmetric routing enforcement).
 .It Cm route-to
 The
 .Cm route-to
-option routes the packet to the specified interface with an optional address
-for the next hop.
+option routes the packet to the specified destination address instead
+of the destination address in the packet header.
 When a
 .Cm route-to
 rule creates state, only packets that pass in the same direction as the
@@ -2858,8 +2858,7 @@ ifspec         = ( [ "!" ] ( interface-name | interface-group ) ) |
 interface-list = [ "!" ] ( interface-name | interface-group )
                  [ [ "," ] interface-list ]
 route          = ( "route-to" | "reply-to" | "dup-to" )
-                 ( routehost | "{" routehost-list "}" )
-                 [ pooltype ]
+                 ( redirhost | "{" redirhost-list "}" )
 af             = "inet" | "inet6"
 
 protospec      = "proto" ( proto-name | proto-number |
@@ -2878,14 +2877,11 @@ host           = [ "!" ] ( address [ "weight" number ] |
                  address [ "/" mask-bits ] [ "weight" number ] |
                  "<" string ">" )
 redirhost      = address [ "/" mask-bits ]
-routehost      = host | host "@" interface-name |
-                 "(" interface-name [ address [ "/" mask-bits ] ] ")"
 address        = ( interface-name | interface-group |
                  "(" ( interface-name | interface-group ) ")" |
                  hostname | ipv4-dotted-quad | ipv6-coloned-hex )
 host-list      = host [ [ "," ] host-list ]
 redirhost-list = redirhost [ [ "," ] redirhost-list ]
-routehost-list = routehost [ [ "," ] routehost-list ]
 
 port           = "port" ( unary-op | binary-op | "{" op-list "}" )
 portspec       = "port" ( number | name ) [ ":" ( "*" | number | name ) ]
index 9f47717..2b094dd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_pfsync.c,v 1.281 2021/01/18 18:29:19 mvs Exp $     */
+/*     $OpenBSD: if_pfsync.c,v 1.282 2021/02/01 00:31:05 dlg Exp $     */
 
 /*
  * Copyright (c) 2002 Michael Shalayeff
@@ -613,6 +613,7 @@ pfsync_state_import(struct pfsync_state *sp, int flags)
 
        /* copy to state */
        st->rt_addr = sp->rt_addr;
+       st->rt = sp->rt;
        st->creation = getuptime() - ntohl(sp->creation);
        st->expire = getuptime();
        if (ntohl(sp->expire)) {
@@ -643,7 +644,6 @@ pfsync_state_import(struct pfsync_state *sp, int flags)
 
        st->rule.ptr = r;
        st->anchor.ptr = NULL;
-       st->rt_kif = NULL;
 
        st->pfsync_time = getuptime();
        st->sync_state = PFSYNC_S_NONE;
@@ -1860,7 +1860,7 @@ pfsync_undefer(struct pfsync_deferral *pd, int drop)
        if (drop)
                m_freem(pd->pd_m);
        else {
-               if (st->rule.ptr->rt == PF_ROUTETO) {
+               if (st->rt == PF_ROUTETO) {
                        if (pf_setup_pdesc(&pdesc, st->key[PF_SK_WIRE]->af,
                            st->direction, st->kif, pd->pd_m, NULL) !=
                            PF_PASS) {
@@ -1869,11 +1869,11 @@ pfsync_undefer(struct pfsync_deferral *pd, int drop)
                        }
                        switch (st->key[PF_SK_WIRE]->af) {
                        case AF_INET:
-                               pf_route(&pdesc, st->rule.ptr, st);
+                               pf_route(&pdesc, st);
                                break;
 #ifdef INET6
                        case AF_INET6:
-                               pf_route6(&pdesc, st->rule.ptr, st);
+                               pf_route6(&pdesc, st);
                                break;
 #endif /* INET6 */
                        default:
index 0f07fe9..9c25d42 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pf.c,v 1.1105 2021/01/28 09:37:20 dlg Exp $ */
+/*     $OpenBSD: pf.c,v 1.1106 2021/02/01 00:31:05 dlg Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -1180,6 +1180,7 @@ pf_state_export(struct pfsync_state *sp, struct pf_state *st)
 
        /* copy from state */
        strlcpy(sp->ifname, st->kif->pfik_name, sizeof(sp->ifname));
+       sp->rt = st->rt;
        sp->rt_addr = st->rt_addr;
        sp->creation = htonl(getuptime() - st->creation);
        expire = pf_state_expires(st);
@@ -3430,16 +3431,13 @@ pf_set_rt_ifp(struct pf_state *s, struct pf_addr *saddr, sa_family_t af,
        struct pf_rule *r = s->rule.ptr;
        int     rv;
 
-       s->rt_kif = NULL;
        if (!r->rt)
                return (0);
 
        rv = pf_map_addr(af, r, saddr, &s->rt_addr, NULL, sns, 
            &r->route, PF_SN_ROUTE);
-       if (rv == 0) {
-               s->rt_kif = r->route.kif;
-               s->natrule.ptr = r;
-       }
+       if (rv == 0)
+               s->rt = r->rt;
 
        return (rv);
 }
@@ -5963,15 +5961,13 @@ pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw,
 
 /* pf_route() may change pd->m, adjust local copies after calling */
 void
-pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
+pf_route(struct pf_pdesc *pd, struct pf_state *s)
 {
        struct mbuf             *m0, *m1;
        struct sockaddr_in      *dst, sin;
        struct rtentry          *rt = NULL;
        struct ip               *ip;
        struct ifnet            *ifp = NULL;
-       struct pf_addr           naddr;
-       struct pf_src_node      *sns[PF_SN_MAX];
        int                      error = 0;
        unsigned int             rtableid;
 
@@ -5981,11 +5977,11 @@ pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
                return;
        }
 
-       if (r->rt == PF_DUPTO) {
+       if (s->rt == PF_DUPTO) {
                if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL)
                        return;
        } else {
-               if ((r->rt == PF_REPLYTO) == (r->direction == pd->dir))
+               if ((s->rt == PF_REPLYTO) == (s->direction == pd->dir))
                        return;
                m0 = pd->m;
                pd->m = NULL;
@@ -5999,48 +5995,45 @@ pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
 
        ip = mtod(m0, struct ip *);
 
-       memset(&sin, 0, sizeof(sin));
-       dst = &sin;
-       dst->sin_family = AF_INET;
-       dst->sin_len = sizeof(*dst);
-       dst->sin_addr = ip->ip_dst;
-       rtableid = m0->m_pkthdr.ph_rtableid;
-
        if (pd->dir == PF_IN) {
                if (ip->ip_ttl <= IPTTLDEC) {
-                       if (r->rt != PF_DUPTO)
+                       if (s->rt != PF_DUPTO) {
                                pf_send_icmp(m0, ICMP_TIMXCEED,
                                    ICMP_TIMXCEED_INTRANS, 0,
-                                   pd->af, r, pd->rdomain);
+                                   pd->af, s->rule.ptr, pd->rdomain);
+                       }
                        goto bad;
                }
                ip->ip_ttl -= IPTTLDEC;
        }
 
-       if (s == NULL) {
-               memset(sns, 0, sizeof(sns));
-               if (pf_map_addr(AF_INET, r,
-                   (struct pf_addr *)&ip->ip_src,
-                   &naddr, NULL, sns, &r->route, PF_SN_ROUTE)) {
-                       DPFPRINTF(LOG_ERR,
-                           "%s: pf_map_addr() failed", __func__);
-                       goto bad;
-               }
+       memset(&sin, 0, sizeof(sin));
+       dst = &sin;
+       dst->sin_family = AF_INET;
+       dst->sin_len = sizeof(*dst);
+       dst->sin_addr = s->rt_addr.v4;
+       rtableid = m0->m_pkthdr.ph_rtableid;
 
-               if (!PF_AZERO(&naddr, AF_INET))
-                       dst->sin_addr.s_addr = naddr.v4.s_addr;
-               ifp = r->route.kif ?
-                   r->route.kif->pfik_ifp : NULL;
-       } else {
-               if (!PF_AZERO(&s->rt_addr, AF_INET))
-                       dst->sin_addr.s_addr =
-                           s->rt_addr.v4.s_addr;
-               ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
+       rt = rtalloc(sintosa(dst), RT_RESOLVE, rtableid);
+       if (!rtisvalid(rt)) {
+               if (s->rt != PF_DUPTO) {
+                       pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_HOST,
+                           0, pd->af, s->rule.ptr, pd->rdomain);
+               }
+               ipstat_inc(ips_noroute);
+               goto bad;
        }
+
+       ifp = if_get(rt->rt_ifidx);
        if (ifp == NULL)
                goto bad;
 
-       if (r->rt != PF_DUPTO && pd->kif->pfik_ifp != ifp) {
+       /* A locally generated packet may have invalid source address. */
+       if ((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET &&
+           (ifp->if_flags & IFF_LOOPBACK) == 0)
+               ip->ip_src = ifatoia(rt->rt_ifa)->ia_addr.sin_addr;
+
+       if (s->rt != PF_DUPTO && pd->kif->pfik_ifp != ifp) {
                if (pf_test(AF_INET, PF_OUT, ifp, &m0) != PF_PASS)
                        goto bad;
                else if (m0 == NULL)
@@ -6053,20 +6046,6 @@ pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
                ip = mtod(m0, struct ip *);
        }
 
-       rt = rtalloc(sintosa(dst), RT_RESOLVE, rtableid);
-       if (!rtisvalid(rt)) {
-               if (r->rt != PF_DUPTO) {
-                       pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_HOST,
-                           0, pd->af, s->rule.ptr, pd->rdomain);
-               }
-               ipstat_inc(ips_noroute);
-               goto bad;
-       }
-       /* A locally generated packet may have invalid source address. */
-       if ((ntohl(ip->ip_src.s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET &&
-           (ifp->if_flags & IFF_LOOPBACK) == 0)
-               ip->ip_src = ifatoia(rt->rt_ifa)->ia_addr.sin_addr;
-
        in_proto_cksum_out(m0, ifp);
 
        if (ntohs(ip->ip_len) <= ifp->if_mtu) {
@@ -6087,9 +6066,9 @@ pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
         */
        if (ip->ip_off & htons(IP_DF)) {
                ipstat_inc(ips_cantfrag);
-               if (r->rt != PF_DUPTO)
+               if (s->rt != PF_DUPTO)
                        pf_send_icmp(m0, ICMP_UNREACH, ICMP_UNREACH_NEEDFRAG,
-                           ifp->if_mtu, pd->af, r, pd->rdomain);
+                           ifp->if_mtu, pd->af, s->rule.ptr, pd->rdomain);
                goto bad;
        }
 
@@ -6113,6 +6092,7 @@ pf_route(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
                ipstat_inc(ips_fragmented);
 
 done:
+       if_put(ifp);
        rtfree(rt);
        return;
 
@@ -6124,15 +6104,13 @@ bad:
 #ifdef INET6
 /* pf_route6() may change pd->m, adjust local copies after calling */
 void
-pf_route6(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
+pf_route6(struct pf_pdesc *pd, struct pf_state *s)
 {
        struct mbuf             *m0;
        struct sockaddr_in6     *dst, sin6;
        struct rtentry          *rt = NULL;
        struct ip6_hdr          *ip6;
        struct ifnet            *ifp = NULL;
-       struct pf_addr           naddr;
-       struct pf_src_node      *sns[PF_SN_MAX];
        struct m_tag            *mtag;
        unsigned int             rtableid;
 
@@ -6142,11 +6120,11 @@ pf_route6(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
                return;
        }
 
-       if (r->rt == PF_DUPTO) {
+       if (s->rt == PF_DUPTO) {
                if ((m0 = m_dup_pkt(pd->m, max_linkhdr, M_NOWAIT)) == NULL)
                        return;
        } else {
-               if ((r->rt == PF_REPLYTO) == (r->direction == pd->dir))
+               if ((s->rt == PF_REPLYTO) == (s->direction == pd->dir))
                        return;
                m0 = pd->m;
                pd->m = NULL;
@@ -6159,74 +6137,59 @@ pf_route6(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
        }
        ip6 = mtod(m0, struct ip6_hdr *);
 
-       memset(&sin6, 0, sizeof(sin6));
-       dst = &sin6;
-       dst->sin6_family = AF_INET6;
-       dst->sin6_len = sizeof(*dst);
-       dst->sin6_addr = ip6->ip6_dst;
-       rtableid = m0->m_pkthdr.ph_rtableid;
-
        if (pd->dir == PF_IN) {
                if (ip6->ip6_hlim <= IPV6_HLIMDEC) {
-                       if (r->rt != PF_DUPTO)
+                       if (s->rt != PF_DUPTO) {
                                pf_send_icmp(m0, ICMP6_TIME_EXCEEDED,
                                    ICMP6_TIME_EXCEED_TRANSIT, 0,
-                                   pd->af, r, pd->rdomain);
+                                   pd->af, s->rule.ptr, pd->rdomain);
+                       }
                        goto bad;
                }
                ip6->ip6_hlim -= IPV6_HLIMDEC;
        }
 
-       if (s == NULL) {
-               memset(sns, 0, sizeof(sns));
-               if (pf_map_addr(AF_INET6, r, (struct pf_addr *)&ip6->ip6_src,
-                   &naddr, NULL, sns, &r->route, PF_SN_ROUTE)) {
-                       DPFPRINTF(LOG_ERR,
-                           "%s: pf_map_addr() failed", __func__);
-                       goto bad;
-               }
-               if (!PF_AZERO(&naddr, AF_INET6))
-                       pf_addrcpy((struct pf_addr *)&dst->sin6_addr,
-                           &naddr, AF_INET6);
-               ifp = r->route.kif ? r->route.kif->pfik_ifp : NULL;
-       } else {
-               if (!PF_AZERO(&s->rt_addr, AF_INET6))
-                       pf_addrcpy((struct pf_addr *)&dst->sin6_addr,
-                           &s->rt_addr, AF_INET6);
-               ifp = s->rt_kif ? s->rt_kif->pfik_ifp : NULL;
-       }
-       if (ifp == NULL)
-               goto bad;
-
-       if (r->rt != PF_DUPTO && pd->kif->pfik_ifp != ifp) {
-               if (pf_test(AF_INET6, PF_OUT, ifp, &m0) != PF_PASS)
-                       goto bad;
-               else if (m0 == NULL)
-                       goto done;
-               if (m0->m_len < sizeof(struct ip6_hdr)) {
-                       DPFPRINTF(LOG_ERR,
-                           "%s: m0->m_len < sizeof(struct ip6_hdr)", __func__);
-                       goto bad;
-               }
-       }
+       memset(&sin6, 0, sizeof(sin6));
+       dst = &sin6;
+       dst->sin6_family = AF_INET6;
+       dst->sin6_len = sizeof(*dst);
+       dst->sin6_addr = s->rt_addr.v6;
+       rtableid = m0->m_pkthdr.ph_rtableid;
 
        if (IN6_IS_SCOPE_EMBED(&dst->sin6_addr))
                dst->sin6_addr.s6_addr16[1] = htons(ifp->if_index);
        rt = rtalloc(sin6tosa(dst), RT_RESOLVE, rtableid);
        if (!rtisvalid(rt)) {
-               if (r->rt != PF_DUPTO) {
+               if (s->rt != PF_DUPTO) {
                        pf_send_icmp(m0, ICMP6_DST_UNREACH,
                            ICMP6_DST_UNREACH_NOROUTE, 0,
                            pd->af, s->rule.ptr, pd->rdomain);
-               }
+               }
                ip6stat_inc(ip6s_noroute);
                goto bad;
        }
+
+       ifp = if_get(rt->rt_ifidx);
+       if (ifp == NULL)
+               goto bad;
+
        /* A locally generated packet may have invalid source address. */
        if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) &&
            (ifp->if_flags & IFF_LOOPBACK) == 0)
                ip6->ip6_src = ifatoia6(rt->rt_ifa)->ia_addr.sin6_addr;
 
+       if (s->rt != PF_DUPTO && pd->kif->pfik_ifp != ifp) {
+               if (pf_test(AF_INET6, PF_OUT, ifp, &m0) != PF_PASS)
+                       goto bad;
+               else if (m0 == NULL)
+                       goto done;
+               if (m0->m_len < sizeof(struct ip6_hdr)) {
+                       DPFPRINTF(LOG_ERR,
+                           "%s: m0->m_len < sizeof(struct ip6_hdr)", __func__);
+                       goto bad;
+               }
+       }
+
        in6_proto_cksum_out(m0, ifp);
 
        /*
@@ -6239,13 +6202,14 @@ pf_route6(struct pf_pdesc *pd, struct pf_rule *r, struct pf_state *s)
                ifp->if_output(ifp, m0, sin6tosa(dst), rt);
        } else {
                ip6stat_inc(ip6s_cantfrag);
-               if (r->rt != PF_DUPTO)
+               if (s->rt != PF_DUPTO)
                        pf_send_icmp(m0, ICMP6_PACKET_TOO_BIG, 0,
-                           ifp->if_mtu, pd->af, r, pd->rdomain);
+                           ifp->if_mtu, pd->af, s->rule.ptr, pd->rdomain);
                goto bad;
        }
 
 done:
+       if_put(ifp);
        rtfree(rt);
        return;
 
@@ -7286,14 +7250,14 @@ done:
                pd.m = NULL;
                break;
        default:
-               if (r->rt) {
+               if (s && s->rt) {
                        switch (pd.af) {
                        case AF_INET:
-                               pf_route(&pd, r, s);
+                               pf_route(&pd, s);
                                break;
 #ifdef INET6
                        case AF_INET6:
-                               pf_route6(&pd, r, s);
+                               pf_route6(&pd, s);
                                break;
 #endif /* INET6 */
                        }
index ef80398..ae77428 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pfvar.h,v 1.498 2021/01/12 00:10:34 bluhm Exp $ */
+/*     $OpenBSD: pfvar.h,v 1.499 2021/02/01 00:31:05 dlg Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -762,7 +762,6 @@ struct pf_state {
        struct pf_sn_head        src_nodes;
        struct pf_state_key     *key[2];        /* addresses stack and wire  */
        struct pfi_kif          *kif;
-       struct pfi_kif          *rt_kif;
        u_int64_t                packets[2];
        u_int64_t                bytes[2];
        int32_t                  creation;
@@ -797,6 +796,7 @@ struct pf_state {
        u_int16_t                if_index_out;
        pf_refcnt_t              refcnt;
        u_int16_t                delay;
+       u_int8_t                 rt;
 };
 
 /*
@@ -852,7 +852,7 @@ struct pfsync_state {
        u_int8_t         proto;
        u_int8_t         direction;
        u_int8_t         log;
-       u_int8_t         pad0;
+       u_int8_t         rt;
        u_int8_t         timeout;
        u_int8_t         sync_flags;
        u_int8_t         updates;
@@ -1798,8 +1798,8 @@ int       pf_state_key_attach(struct pf_state_key *, struct pf_state *, int);
 int    pf_translate(struct pf_pdesc *, struct pf_addr *, u_int16_t,
            struct pf_addr *, u_int16_t, u_int16_t, int);
 int    pf_translate_af(struct pf_pdesc *);
-void   pf_route(struct pf_pdesc *, struct pf_rule *, struct pf_state *);
-void   pf_route6(struct pf_pdesc *, struct pf_rule *, struct pf_state *);
+void   pf_route(struct pf_pdesc *, struct pf_state *);
+void   pf_route6(struct pf_pdesc *, struct pf_state *);
 void   pf_init_threshold(struct pf_threshold *, u_int32_t, u_int32_t);
 int    pf_delay_pkt(struct mbuf *, u_int);