vas.c revision 7663b81667fda05833f609eceac713f0a83c2347
/*
* Copyright (C) 2003 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <stropts.h>
#include <sys/sysmacros.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/systeminfo.h>
#include "pfild.h"
#ifndef MIN
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif
/*
* vas.c: Valid Address Set computation and communication for pfild
*
* pfild computes a "valid source address set" for each interface and hands the
* resulting data to the pfil module which makes it available to pfil clients.
* The ipf module uses the valid address sets to implement the fr_chksrc
* feature (automatic protection from source address spoofing).
*
* A valid source address for a packet received on given interface is defined
* as an address which, if used as a destination, would be routed to that
* interface. This code assumes that only inbound traffic will be tested
* against the valid address sets; thus all local and loopback addresses are
* considered invalid.
*
* The TPI MIB interface is used to read the current routing table. A
* discarding most of them, but saving the two that contain the IPv4 and IPv6
* routing tables. Also inspected are two other messages that each happen to
* contain a constant needed to parse the routing table messages.
*
* An address set is represented as a sorted list of mutually discontiguous
* non-empty inclusive spans. In the kernel, this list can be efficiently
* binary-searched. In user space, we can compute unions and intersections of
* address sets. In either case, IPv4 addresses are stored in host byte order
* for efficient numerical comparisons. IPv6 addresses will be compared
* byte-at-a-time so they are kept in conventional struct in6_addr form
* (network byte order).
*/
/*
*/
typedef union i6addr {
} i6addr_t;
#define IP6_EQ(a, b) (IN6_ARE_ADDR_EQUAL(a, b))
#define IP6_INC(a) \
} \
} \
} \
}
#define IP6_DEC(a) \
} \
} \
} \
}
#define IP6_FIRST(a, m) \
{ if ((m) > 96) { \
} else if ((m) > 64) { \
I63(a) = 0; \
} else if ((m) > 32) { \
I62(a) = 0; \
I63(a) = 0; \
} else if ((m) > 0) { \
I61(a) = 0; \
I62(a) = 0; \
I63(a) = 0; \
} else { \
I60(a) = 0; \
I61(a) = 0; \
I62(a) = 0; \
I63(a) = 0; \
} \
}
#define IP6_LAST(a, m) \
{ if ((m) == 128) { \
} else if ((m) >= 96) { \
} else if ((m) >= 64) { \
I63(a) = 0xFFFFFFFFU; \
} else if ((m) >= 32) { \
I62(a) = 0xFFFFFFFFU; \
I63(a) = 0xFFFFFFFFU; \
} else if ((m) >= 0) { \
I60(a) |= (0xFFFFFFFF >> (m)); \
I61(a) = 0xFFFFFFFFU; \
I62(a) = 0xFFFFFFFFU; \
I63(a) = 0xFFFFFFFFU; \
} \
}
/*
* User space uses a linked list of spans, rather than the array that is
*/
struct spannode {
union {
struct pfil_v4span v4;
struct pfil_v6span v6;
} span;
};
struct addrset {
const char *name;
};
/*
* Allocate and initialize a new struct addrset.
* Returns pointer to new instance or NULL for allocation failure.
*/
static struct addrset *
{
return (NULL);
return (asp);
}
/*
* Free an addrset instance.
*/
static void
{
}
}
/*
* Add a single IPv4 address or a prefix to a set.
* Returns 0 for success, non-zero for failure (allocation error).
* addr and mask are passed in network byte order, but immediately converted
* to host byte order for comparisons.
*/
static int
{
/*
* Search through the list linearly, looking for either: an entry
* contiguous to the one being added (with which we will merge) or a
* discontiguous entry with a higher address (before which we will
* insert). If no match, we will append at the end.
*/
continue;
/* Merge with this entry. */
/* Merge this span with the next. */
} else {
}
}
return (0);
} else {
/* Found the insertion point; exit the loop. */
break;
}
}
/* ptpn now points to the "previous next" where we need to insert. */
p = malloc(sizeof (*p));
if (p == NULL)
return (1);
*ptpn = p;
return (0);
}
/*
* Remove one range of IPv4 addresses from a set.
*/
static int
{
/*
* Search through the list linearly, looking for any of: an entry
* entirely contained with the range being deleted (which we will
* delete from the list) or an entry overlapping the first address of
* the range (which we will truncate at its end) or an entry
* overlapping the last address of the range (which we will truncate at
* its beginning) or an entry which entirely contains the range being
* deleted plus at least one address beyond in each direction (which we
* will split into two entries) or an entry with a higher address than
* we are deleting (at which point we are done).
*/
return (0); /* all done */
continue; /* keep searching */
/* Delete a span entirely. */
free(p);
p = *ptpn;
return (0); /* all done */
}
/* Truncate a span at its beginning. */
/* Truncate a span at its end. */
} else {
/* Split a span into two. */
return (1);
}
}
return (0);
}
/*
* Add a single IPv6 address or a prefix to a set.
* Returns 0 for success, non-zero for failure (allocation error).
* addr is passed in network byte order, but keep this order.
* prefixlen is the prefix length.
*/
static int
{
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff };
/*
* Search through the list linearly, looking for either: an entry
* contiguous to the one being added (with which we will merge) or a
* discontiguous entry with a higher address (before which we will
* insert). If no match, we will append at the end.
*/
continue;
/* Merge with this entry. */
break;
}
/* Merge this span with the next. */
} else {
}
}
return (0);
} else {
/* Found the insertion point; exit the loop. */
break;
}
}
/* ptpn now points to the "previous next" where we need to insert. */
p = malloc(sizeof (*p));
if (p == NULL)
return (1);
*ptpn = p;
return (0);
}
/*
* Remove one range of IPv6 addresses from a set.
*/
static int
{
/*
* Search through the list linearly, looking for any of: an entry
* entirely contained with the range being deleted (which we will
* delete from the list) or an entry overlapping the first address of
* the range (which we will truncate at its end) or an entry
* overlapping the last address of the range (which we will truncate at
* its beginning) or an entry which entirely contains the range being
* deleted plus at least one address beyond in each direction (which we
* will split into two entries) or an entry with a higher address than
* we are deleting (at which point we are done).
*/
return (0); /* all done */
continue; /* keep searching */
/* Delete a span entirely. */
free(p);
p = *ptpn;
return (0); /* all done */
}
/* Truncate a span at its beginning. */
/* Truncate a span at its end. */
} else {
/* Split a span into two. */
return (1);
}
}
return (0);
}
/*
* Compute the set difference (remove elements in set 2 from set 1).
*/
static void
{
struct spannode *p;
return;
/* For each span in set 2, delete it from set 1. */
(void) addrset_delete_v4(asp1,
(void) addrset_delete_v6(asp1,
}
typedef struct mib_item_s {
int group;
int mib_id;
void *valp;
} mib_item_t;
static int ipRouteEntrySize;
static int ipv6RouteEntrySize;
static mib_item_t *ipv4Table;
static mib_item_t *ipv6Table;
/*
* Copy and NUL-terminate a MIB octet-string.
*/
static void
{
dst[n] = '\0';
}
/*
* Read the whole IP MIB, looking for the routing related entries.
* Save the IPv4 and IPv6 route table items and peek into a couple other
* items to learn the increments between records in the route table items.
*/
static void
{
/*
* buf is an automatic for this function, so the
* compiler has complete control over its alignment;
* it is assumed this alignment is satisfactory for
* it to be casted to certain other struct pointers
* here, such as struct T_optmgmt_ack * .
*/
int flags;
int j, getcode;
flags = 0;
perror("mibget: putmsg(ctl) failed");
goto error_exit;
}
/*
* Each reply consists of a ctl part for one fixed structure
* or table, as defined in mib2.h. The format is a T_OPTMGMT_ACK,
* len is the size of the data part of the message.
*/
j = 1;
for (;;) {
flags = 0;
if (getcode == -1) {
perror("mibget getmsg(ctl) failed");
goto error_exit;
}
if (getcode == 0 &&
return;
"mibget %d gives T_ERROR_ACK: TLI_error = 0x%lx, "
"UNIX_error = 0x%lx\n",
goto error_exit;
}
(void) printf("mibget getmsg(ctl) %d returned %d, "
"ctlbuf.len = %d, PRIM_type = %ld\n",
(void) printf("T_OPTMGMT_ACK: "
"MGMT_flags = 0x%lx, req->len = %ld\n",
goto error_exit;
}
perror("mibget malloc failed");
goto error_exit;
}
goto error_exit;
}
flags = 0;
if (getcode == -1) {
perror("mibload getmsg(data) failed");
goto error_exit;
} else if (getcode != 0) {
(void) printf("mibload getmsg(data) returned %d, "
"databuf.maxlen = %d, databuf.len = %d\n",
goto error_exit;
}
j++;
continue;
}
case MIB2_IP_ROUTE:
if (ipv4Table)
break;
case MIB2_IP6_ROUTE:
if (ipv6Table)
break;
case 0:
/* FALLTHROUGH */
default:
break;
}
}
/* NOTREACHED */
}
/*
* mibfree: frees a (mib_item_t *) loaded by mibload()
*/
static void
{
}
#define IPROUTEENTRYALIGNMENT 4
#define IP6ROUTEENTRYALIGNMENT 4
/* Extract constant sizes. */
static void
{
case MIB2_IP: {
break;
}
case MIB2_IP6: {
/* Just use the first entry */
break;
}
}
}
/*
* Compose a PFILCMD_IFADDRSET message for each interface and deliver them to
* pfil. Returns 0 for success, non-zero for failure.
*/
static int
{
int status = 0, i;
for (i = 0; i < numifs; i++)
struct pfil_v4span *p2;
int nspans = 0;
nspans++;
size = sizeof (struct pfil_ifaddrset) +
nspans * sizeof (struct pfil_v4span);
return (-1);
++p2;
}
if (status != 0)
break;
struct pfil_v6span *p2;
int nspans = 0;
nspans++;
size = sizeof (struct pfil_ifaddrset) +
nspans * sizeof (struct pfil_v6span);
return (-1);
++p2;
}
if (status != 0)
break;
}
return (status);
}
/*
* Find an interface through which the gateway is reachable and return its
* name in the specififed buffer.
*/
static void
{
rp = (mib2_ipRouteEntry_t *)
((char *)rp + ipRouteEntrySize)) {
return;
}
}
outif[0] = '\0';
}
/*
* Find an interface through which the gateway is reachable and return its
* name in the specififed buffer.
*/
static void
{
rp = (mib2_ipv6RouteEntry_t *)
((char *)rp + ipv6RouteEntrySize)) {
return;
}
}
outif[0] = '\0';
}
/*
* Compute the valid address sets for the specified interfaces, then compose a
* series of PFILCMD_IFADDRSET messages and deliver them to pfil. Returns 0 for
* success, non-zero for failure.
*/
int
{
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 };
if (sd == -1)
return (-1);
goto err;
for (i = 0; i < numifs; i++) {
/*
* Both are located in the same address.
*/
goto err;
}
if (illegal_v4 == NULL)
goto err;
/* Multicast addresses are always illegal as source address. */
if (addrset_add_v4(illegal_v4,
goto err;
/* Loopback addresses are illegal on non-loopback interfaces. */
if (addrset_add_v4(illegal_v4,
goto err;
if (illegal_v6 == NULL)
goto err;
/* Multicast addresses are always illegal as source address. */
goto err;
/* Loopback addresses are illegal on non-loopback interfaces. */
goto err;
/* Unspecified addresses are always illegal as source address. */
goto err;
if (ipRouteEntrySize < sizeof (mib2_ipRouteEntry_t) ||
ipv6RouteEntrySize < sizeof (mib2_ipv6RouteEntry_t) ||
err:
status = -1;
goto done;
}
rp = (mib2_ipRouteEntry_t *)
((char *)rp + ipRouteEntrySize)) {
case IRE_CACHE:
continue;
case IRE_BROADCAST:
case IRE_LOCAL:
asp = illegal_v4;
break;
default:
} else {
}
if (outif[0] != '\0') {
for (i = 0; i < numifs; i++) {
LIFNAMSIZ) == 0) {
break;
}
}
}
break;
}
goto err;
}
}
rp = (mib2_ipv6RouteEntry_t *)
((char *)rp + ipv6RouteEntrySize)) {
case IRE_CACHE:
continue;
case IRE_BROADCAST:
case IRE_LOCAL:
asp = illegal_v6;
break;
default:
} else {
}
if (outif[0] != '\0') {
for (i = 0; i < numifs; i++) {
LIFNAMSIZ) == 0) {
break;
}
}
}
break;
}
rp->ipv6RoutePfxLength) != 0)
goto err;
}
}
for (i = 0; i < numifs; i++) {
}
#ifdef DEBUG
#endif
done:
for (i = 0; i < numifs; i++)
delete_addrset(ifs[i]);
if (illegal_v4 != NULL)
if (illegal_v6 != NULL)
return (status);
}
#ifdef DEBUG
static void
{
return;
}
static void
{
} else {
}
} else {
}
}
}
static void
{
struct spannode *p;
(void) puts(" [empty]");
return;
}
while (p != NULL) {
char buf[100];
(void) putchar(' ');
if (p->next)
(void) putchar(',');
p = p->next;
}
(void) putchar('\n');
}
static void
{
int i;
(void) puts(" [empty]");
return;
}
char buf[INET_ADDRSTRLEN];
}
(void) putchar(',');
p++;
}
(void) putchar('\n');
char buf[INET6_ADDRSTRLEN];
}
(void) putchar(',');
p++;
}
(void) putchar('\n');
}
}
int
{
return (0);
}
int
{
int numifs, i;
struct pfil_ifaddrs *ifaddrlist;
return (-1);
for (i = 0; i < numifs; i++) {
}
return (-1);
}
for (i = 0; i < numifs; i++) {
}
return (-1);
}
return (0);
}
#endif