summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorHenning Brauer <henning@cvs.openbsd.org>2002-07-09 10:39:09 +0000
committerHenning Brauer <henning@cvs.openbsd.org>2002-07-09 10:39:09 +0000
commit1732cbb3c4713ada24dbe08acbda42051a89722a (patch)
tree5d18625cc9d99609260a7185758124ef15deadcc /sbin
parent385c9ab1eda4b24cbb699d2717f7c6704f218483 (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@
Diffstat (limited to 'sbin')
-rw-r--r--sbin/pfctl/parse.y250
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;
}