diff options
author | Henning Brauer <henning@cvs.openbsd.org> | 2002-07-09 10:39:09 +0000 |
---|---|---|
committer | Henning Brauer <henning@cvs.openbsd.org> | 2002-07-09 10:39:09 +0000 |
commit | 1732cbb3c4713ada24dbe08acbda42051a89722a (patch) | |
tree | 5d18625cc9d99609260a7185758124ef15deadcc | |
parent | 385c9ab1eda4b24cbb699d2717f7c6704f218483 (diff) |
rework the interface-to-IP routines.
you can use interface names instead of an IP in most places. However, until
now, it was only expanded to the interface's first IPv4 address if existant
(and address family unset or inet) and the first IPv6 address otherwise.
this diff changes that. the interface is proper expanded to all IPs, IPv4
_and_ IPv6, now.
it also cleans up the lookup procedures (well, in fact, they are replaced by
a new one), there's no need for different procedures for IPv4 and IPv6. we
now just have one list of interfaces (AF_LINK) and one list with IPs
(AF_INET and AF_INET6) with corresponding lookup functions, ifa_exists and
ifa_lookup.
nat, rdr & friends now use the new function ifa_pick_ip to get the IP in
rules like
nat on $interface from $whatever to any -> $interface
ifa_pick_ip tries to be smart.
if the interface has only one IP address and the nat rule doesn't specify an
address family (or it matches with this address), take this one.
If the address family is specified in the nat rule and there is only one IP
for the given address family, this one is used. if the address family is not
specified and there is more than one IP pfctl throws an error. The same
applies for multiple IPs per address family.
This causes regression tests 18 and 20 to fail because the address family
isn't specified there; diff for those coming.
also fix some prototypes while I'm here.
pb@ found another problem while testing that we must have introduced somewhat
after 3.1.
$cat t
nat on ne3 from any to any -> 213.128.133.5
$pfctl -nvf t
nat on ne3 all -> ?
it's only a representation bug as far as I've checked, nontheless it should
be fixed. as a nat/rdr rule always nats/redirects to one IP only we can just
steal its target's IP af and set the rule's af accordingly. then inet_ntop
does play nice.
binat rules already enforce having an address family set always and thus are
not affected.
ok dhartmei@, pb@, kjell@
"It looks good" frantzen@
-rw-r--r-- | sbin/pfctl/parse.y | 250 |
1 files changed, 131 insertions, 119 deletions
diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index d803ea7cfa4..421d9d1495d 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.113 2002/07/08 11:46:32 dhartmei Exp $ */ +/* $OpenBSD: parse.y,v 1.114 2002/07/09 10:39:08 henning Exp $ */ /* * Copyright (c) 2001 Markus Friedl. All rights reserved. @@ -151,12 +151,13 @@ struct sym { }; struct sym *symhead = NULL; -int symset(const char *name, const char *val); -char * symget(const char *name); +int symset(const char *, const char *); +char * symget(const char *); -struct ifaddrs *ifa0_lookup(char *ifa_name); -struct ifaddrs *ifa4_lookup(char *ifa_name); -struct ifaddrs *ifa6_lookup(char *ifa_name); +void ifa_load(); +int ifa_exists(char *); +struct node_host *ifa_lookup(char *); +struct node_host *ifa_pick_ip(struct node_host *, u_int8_t); typedef struct { union { @@ -513,7 +514,7 @@ if_item_not : '!' if_item { $$ = $2; $$->not = 1; } | if_item { $$ = $1; } if_item : STRING { - if (ifa0_lookup($1) == 0) { + if (!ifa_exists($1)) { yyerror("unknown interface %s", $1); YYERROR; } @@ -659,41 +660,14 @@ address : '(' STRING ')' { sizeof($$->addr.addr.pfa.ifname)); } | STRING { - if (ifa0_lookup($1)) { - struct ifaddrs *ifa; - - /* an interface with this name exists */ - if ((ifa = ifa4_lookup($1))) { - struct sockaddr_in *sin = - (struct sockaddr_in *) - ifa->ifa_addr; - - $$ = calloc(1, - sizeof(struct node_host)); - if ($$ == NULL) - err(1, "address: calloc"); - $$->af = AF_INET; - $$->addr.addr_dyn = NULL; - memcpy(&$$->addr.addr, &sin->sin_addr, - sizeof(u_int32_t)); - } else if ((ifa = ifa6_lookup($1))) { - struct sockaddr_in6 *sin6 = - (struct sockaddr_in6 *) - ifa->ifa_addr; - - $$ = calloc(1, - sizeof(struct node_host)); - if ($$ == NULL) - err(1, "address: calloc"); - $$->af = AF_INET6; - $$->addr.addr_dyn = NULL; - memcpy(&$$->addr.addr, &sin6->sin6_addr, - sizeof(struct pf_addr)); - } else { - yyerror("interface %s has no IP " - "addresses", $1); + if (ifa_exists($1)) { + struct node_host *h = NULL; + + /* interface with this name exists */ + if ((h = ifa_lookup($1)) == NULL) YYERROR; - } + else + $$ = h; } else { struct node_host *h = NULL, *n; struct addrinfo hints, *res0, *res; @@ -1235,10 +1209,6 @@ redirection : /* empty */ { $$ = NULL; } $$ = malloc(sizeof(struct redirection)); if ($$ == NULL) err(1, "redirection: malloc"); - if ($2->next) { - yyerror("multiple ip addresses"); - YYERROR; - } $$->address = $2; $$->rport.a = $$->rport.b = $$->rport.t = 0; } @@ -1246,10 +1216,6 @@ redirection : /* empty */ { $$ = NULL; } $$ = malloc(sizeof(struct redirection)); if ($$ == NULL) err(1, "redirection: malloc"); - if ($2->next) { - yyerror("multiple ip addresses"); - YYERROR; - } $$->address = $2; $$->rport = $4; } @@ -1278,15 +1244,24 @@ natrule : no NAT interface af proto fromto redirection } if (nat.no) { if ($7 != NULL) { - yyerror("'no nat' rule does not need '->'"); + yyerror("'no nat' rule does not need " + "'->'"); YYERROR; } } else { + struct node_host *n; + if ($7 == NULL || $7->address == NULL) { - yyerror("'nat' rule requires '-> address'"); + yyerror("'nat' rule requires '-> " + "address'"); YYERROR; } - memcpy(&nat.raddr, &$7->address->addr, + n = ifa_pick_ip($7->address, nat.af); + if (n == NULL) + YYERROR; + if (!nat.af) + nat.af = n->af; + memcpy(&nat.raddr, &n->addr, sizeof(nat.raddr)); nat.proxy_port[0] = ntohs($7->rport.a); nat.proxy_port[1] = ntohs($7->rport.b); @@ -1385,25 +1360,30 @@ binatrule : no BINAT interface af proto FROM address TO ipspec redirection YYERROR; } } else { + struct node_host *n; + if ($10 == NULL || $10->address == NULL) { yyerror("'binat' rule requires" " '-> address'"); YYERROR; } - if ($10->address->addr.addr_dyn != NULL) { + n = ifa_pick_ip($10->address, binat.af); + if (n == NULL) + YYERROR; + if (n->addr.addr_dyn != NULL) { if (!binat.af) { yyerror("address family (inet/" "inet6) undefined"); YYERROR; } - $10->address->af = binat.af; + n->af = binat.af; } - if (binat.af && $10->address->af != binat.af) { + if (binat.af && n->af != binat.af) { yyerror("binat ip versions must match"); YYERROR; } - binat.af = $10->address->af; - memcpy(&binat.raddr, &$10->address->addr, + binat.af = n->af; + memcpy(&binat.raddr, &n->addr, sizeof(binat.raddr)); free($10->address); free($10); @@ -1457,11 +1437,19 @@ rdrrule : no RDR interface af proto FROM ipspec TO ipspec dport redirection YYERROR; } } else { + struct node_host *n; + if ($11 == NULL || $11->address == NULL) { - yyerror("'rdr' rule requires '-> address'"); + yyerror("'rdr' rule requires '-> " + "address'"); YYERROR; } - memcpy(&rdr.raddr, &$11->address->addr, + n = ifa_pick_ip($11->address, rdr.af); + if (n == NULL) + YYERROR; + if (!rdr.af) + rdr.af = n->af; + memcpy(&rdr.raddr, &n->addr, sizeof(rdr.raddr)); free($11->address); rdr.rport = $11->rport.a; @@ -2530,9 +2518,8 @@ symget(const char *nam) return (NULL); } -struct ifaddrs **ifa0tab, **ifa4tab, **ifa6tab; -int ifa0len, ifa4len, ifa6len; - +struct ifaddrs **ifatab, **ifalist; +int ifatablen, ifalistlen; int ifa_comp(const void *p1, const void *p2) { @@ -2547,95 +2534,120 @@ ifa_load(void) { struct ifaddrs *ifap, *ifa; void *p; - int ifalen = 0; + int load_ifalen = 0; if (getifaddrs(&ifap) < 0) err(1, "getifaddrs"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) - ifalen++; + load_ifalen++; /* (over-)allocate tables */ - ifa0tab = malloc(ifalen * sizeof(void *)); - ifa4tab = malloc(ifalen * sizeof(void *)); - ifa6tab = malloc(ifalen * sizeof(void *)); - if (!ifa0tab || !ifa4tab || !ifa6tab) + ifatab = malloc(load_ifalen * sizeof(void *)); + ifalist = malloc(load_ifalen * sizeof(void *)); + if (!ifatab || !ifalist) err(1, "malloc"); for (ifa = ifap; ifa; ifa = ifa->ifa_next) { if (ifa->ifa_addr->sa_family == AF_LINK) { - if (bsearch(&ifa, ifa0tab, ifa0len, sizeof(void *), + if (bsearch(&ifa, ifalist, ifalistlen, sizeof(void *), ifa_comp)) continue; /* take only the first LINK address */ - ifa0tab[ifa0len++] = ifa; - qsort(ifa0tab, ifa0len, sizeof(void *), ifa_comp); + ifalist[ifalistlen++] = ifa; + qsort(ifalist, ifalistlen, sizeof(void *), ifa_comp); } - if (ifa->ifa_addr->sa_family == AF_INET) { - if (bsearch(&ifa, ifa4tab, ifa4len, sizeof(void *), - ifa_comp)) - continue; /* take only the first IPv4 address */ - ifa4tab[ifa4len++] = ifa; - qsort(ifa4tab, ifa4len, sizeof(void *), ifa_comp); - } - if (ifa->ifa_addr->sa_family == AF_INET6) { - /* XXX - better address selection required! */ - if (bsearch(&ifa, ifa6tab, ifa6len, sizeof(void *), - ifa_comp)) - continue; /* take only the first IPv6 address */ - ifa6tab[ifa6len++] = ifa; - qsort(ifa6tab, ifa6len, sizeof(void *), ifa_comp); + if (ifa->ifa_addr->sa_family == AF_INET || + ifa->ifa_addr->sa_family == AF_INET6) { + ifatab[ifatablen++] = ifa; } } /* shrink tables */ - if ((p = realloc(ifa0tab, ifa0len * sizeof(void *))) == NULL) { - free(ifa0tab); - ifa0tab = NULL; + if ((p = realloc(ifatab, ifatablen * sizeof(void *))) == NULL) { + free(ifatab); + ifatab = NULL; } else - ifa0tab = p; - if ((p = realloc(ifa4tab, ifa4len * sizeof(void *))) == NULL) { - free(ifa4tab); - ifa4tab = NULL; + ifatab = p; + if ((p = realloc(ifalist, ifalistlen * sizeof(void *))) == NULL) { + free(ifalist); + ifalist = NULL; } else - ifa4tab = p; - if ((p = realloc(ifa6tab, ifa6len * sizeof(void *))) == NULL) { - free(ifa6tab); - ifa6tab = NULL; - } else - ifa6tab = p; - if (!ifa0tab || !ifa4tab || !ifa6tab) + ifalist = p; + if (!ifatab || !ifalist) err(1, "realloc"); + } -struct ifaddrs * -ifa0_lookup(char *ifa_name) +int +ifa_exists(char *ifa_name) { struct ifaddrs ifa, *ifp = &ifa, **ifpp; - if (!ifa0tab) + if (!ifalist) ifa_load(); ifa.ifa_name = ifa_name; - ifpp = bsearch(&ifp, ifa0tab, ifa0len, sizeof(void *), ifa_comp); - return ifpp ? *ifpp : NULL; + ifpp = bsearch(&ifp, ifalist, ifalistlen, sizeof(void *), ifa_comp); + if (ifpp == NULL) + return(0); + else + return(1); } -struct ifaddrs * -ifa4_lookup(char *ifa_name) +struct node_host * +ifa_lookup(char *ifa_name) { - struct ifaddrs ifa, *ifp = &ifa, **ifpp; + struct node_host *h = NULL, *n = NULL; + struct ifaddrs *ifa; - if (!ifa4tab) + if (!ifatab) ifa_load(); - ifa.ifa_name = ifa_name; - ifpp = bsearch(&ifp, ifa4tab, ifa4len, sizeof(void *), ifa_comp); - return ifpp ? *ifpp : NULL; + for (ifa = *ifatab; ifa; ifa = ifa->ifa_next) { + if (strncmp(ifa->ifa_name, ifa_name, IFNAMSIZ) == 0) { + if (!(ifa->ifa_addr->sa_family == AF_INET || + ifa->ifa_addr->sa_family == AF_INET6)) + continue; + n = calloc(1, sizeof(struct node_host)); + if (n == NULL) + err(1, "address: calloc"); + n->af = ifa->ifa_addr->sa_family; + n->addr.addr_dyn = NULL; + if (ifa->ifa_addr->sa_family == AF_INET) + memcpy(&n->addr.addr, &((struct sockaddr_in *) + ifa->ifa_addr)->sin_addr.s_addr, + sizeof(struct in_addr)); + else { + memcpy(&n->addr.addr, &((struct sockaddr_in6 *) + ifa->ifa_addr)->sin6_addr.s6_addr, + sizeof(struct in6_addr)); + n->ifindex = ((struct sockaddr_in6 *) + ifa->ifa_addr)->sin6_scope_id; + } + n->next = h; + h = n; + } + } + if (h == NULL) { + yyerror("no IP address found for %s", ifa_name); + } + return (h); } -struct ifaddrs * -ifa6_lookup(char *ifa_name) +struct node_host * +ifa_pick_ip(struct node_host *nh, u_int8_t af) { - struct ifaddrs ifa, *ifp = &ifa, **ifpp; + struct node_host *h, *n = NULL; - if (!ifa6tab) - ifa_load(); - ifa.ifa_name = ifa_name; - ifpp = bsearch(&ifp, ifa6tab, ifa6len, sizeof(void *), ifa_comp); - return ifpp ? *ifpp : NULL; + if (af == 0 && nh->next) { + yyerror("address family not given and interface has multiple " + "ips"); + return(NULL); + } + for(h = nh; h; h = h->next) { + if (h->af == af || h->af == 0 || af == 0) { + if (n != NULL) { + yyerror("interface has multiple IPs of " + "this address family"); + return(NULL); + } + n = h; + } + } + return n; } |