mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-22 04:24:58 +02:00
ipset: import ipset 5.2+GENL
This requires Linux 2.6.35 or newer to build, so it is deactivated by default in the "mconfig" file.
This commit is contained in:
568
extensions/ipset-5/libipset/data.c
Normal file
568
extensions/ipset-5/libipset/data.c
Normal file
@@ -0,0 +1,568 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <assert.h> /* assert */
|
||||
#include <arpa/inet.h> /* ntoh* */
|
||||
#include <net/ethernet.h> /* ETH_ALEN */
|
||||
#include <sys/socket.h> /* AF_ */
|
||||
#include <stdlib.h> /* malloc, free */
|
||||
#include <string.h> /* memset */
|
||||
|
||||
#include <libipset/linux_ip_set.h> /* IPSET_MAXNAMELEN */
|
||||
#include <libipset/debug.h> /* D() */
|
||||
#include <libipset/types.h> /* struct ipset_type */
|
||||
#include <libipset/utils.h> /* inXcpy */
|
||||
#include <libipset/data.h> /* prototypes */
|
||||
|
||||
/* Internal data structure to hold
|
||||
* a) input data entered by the user or
|
||||
* b) data received from kernel
|
||||
*
|
||||
* We always store the data in host order, *except* IP addresses.
|
||||
*/
|
||||
|
||||
struct ipset_data {
|
||||
/* Option bits: which fields are set */
|
||||
uint64_t bits;
|
||||
/* Option bits: which options are ignored */
|
||||
uint64_t ignored;
|
||||
/* Setname */
|
||||
char setname[IPSET_MAXNAMELEN];
|
||||
/* Set type */
|
||||
const struct ipset_type *type;
|
||||
/* Common CADT options */
|
||||
uint8_t cidr;
|
||||
uint8_t family;
|
||||
uint32_t flags; /* command level flags */
|
||||
uint32_t cadt_flags; /* data level flags */
|
||||
uint32_t timeout;
|
||||
union nf_inet_addr ip;
|
||||
union nf_inet_addr ip_to;
|
||||
uint16_t port;
|
||||
uint16_t port_to;
|
||||
union {
|
||||
/* RENAME/SWAP */
|
||||
char setname2[IPSET_MAXNAMELEN];
|
||||
/* CREATE/LIST/SAVE */
|
||||
struct {
|
||||
uint8_t probes;
|
||||
uint8_t resize;
|
||||
uint8_t netmask;
|
||||
uint32_t hashsize;
|
||||
uint32_t maxelem;
|
||||
uint32_t gc;
|
||||
uint32_t size;
|
||||
/* Filled out by kernel */
|
||||
uint32_t references;
|
||||
uint32_t elements;
|
||||
uint32_t memsize;
|
||||
char typename[IPSET_MAXNAMELEN];
|
||||
uint8_t revision_min;
|
||||
uint8_t revision;
|
||||
} create;
|
||||
/* ADT/LIST/SAVE */
|
||||
struct {
|
||||
union nf_inet_addr ip2;
|
||||
uint8_t cidr2;
|
||||
uint8_t proto;
|
||||
char ether[ETH_ALEN];
|
||||
char name[IPSET_MAXNAMELEN];
|
||||
char nameref[IPSET_MAXNAMELEN];
|
||||
} adt;
|
||||
};
|
||||
};
|
||||
|
||||
static void
|
||||
copy_addr(uint8_t family, union nf_inet_addr *ip, const void *value)
|
||||
{
|
||||
if (family == AF_INET)
|
||||
in4cpy(&ip->in, value);
|
||||
else
|
||||
in6cpy(&ip->in6, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_strlcpy - copy the string from src to dst
|
||||
* @dst: the target string buffer
|
||||
* @src: the source string buffer
|
||||
* @len: the length of bytes to copy, including the terminating null byte.
|
||||
*
|
||||
* Copy the string from src to destination, but at most len bytes are
|
||||
* copied. The target is unconditionally terminated by the null byte.
|
||||
*/
|
||||
void
|
||||
ipset_strlcpy(char *dst, const char *src, size_t len)
|
||||
{
|
||||
assert(dst);
|
||||
assert(src);
|
||||
|
||||
strncpy(dst, src, len);
|
||||
dst[len - 1] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_flags_test - test option bits in the data blob
|
||||
* @data: data blob
|
||||
* @flags: the option flags to test
|
||||
*
|
||||
* Returns true if the options are already set in the data blob.
|
||||
*/
|
||||
bool
|
||||
ipset_data_flags_test(const struct ipset_data *data, uint64_t flags)
|
||||
{
|
||||
assert(data);
|
||||
return !!(data->bits & flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_flags_set - set option bits in the data blob
|
||||
* @data: data blob
|
||||
* @flags: the option flags to set
|
||||
*
|
||||
* The function sets the flags in the data blob so that
|
||||
* the corresponding fields are regarded as if filled with proper data.
|
||||
*/
|
||||
void
|
||||
ipset_data_flags_set(struct ipset_data *data, uint64_t flags)
|
||||
{
|
||||
assert(data);
|
||||
data->bits |= flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_flags_unset - unset option bits in the data blob
|
||||
* @data: data blob
|
||||
* @flags: the option flags to unset
|
||||
*
|
||||
* The function unsets the flags in the data blob.
|
||||
* This is the quick way to clear specific fields.
|
||||
*/
|
||||
void
|
||||
ipset_data_flags_unset(struct ipset_data *data, uint64_t flags)
|
||||
{
|
||||
assert(data);
|
||||
data->bits &= ~flags;
|
||||
}
|
||||
|
||||
#define flag_type_attr(data, opt, flag) \
|
||||
do { \
|
||||
data->flags |= flag; \
|
||||
opt = IPSET_OPT_FLAGS; \
|
||||
} while (0)
|
||||
|
||||
#define cadt_flag_type_attr(data, opt, flag) \
|
||||
do { \
|
||||
data->cadt_flags |= flag; \
|
||||
opt = IPSET_OPT_CADT_FLAGS; \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* ipset_data_ignored - test and set ignored bits in the data blob
|
||||
* @data: data blob
|
||||
* @flags: the option flag to be ignored
|
||||
*
|
||||
* Returns true if the option was not already ignored.
|
||||
*/
|
||||
bool
|
||||
ipset_data_ignored(struct ipset_data *data, enum ipset_opt opt)
|
||||
{
|
||||
bool ignored;
|
||||
assert(data);
|
||||
|
||||
ignored = data->ignored & IPSET_FLAG(opt);
|
||||
data->ignored |= IPSET_FLAG(opt);
|
||||
|
||||
return ignored;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_set - put data into the data blob
|
||||
* @data: data blob
|
||||
* @opt: the option kind of the data
|
||||
* @value: the value of the data
|
||||
*
|
||||
* Put a given kind of data into the data blob and mark the
|
||||
* option kind as already set in the blob.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_data_set(struct ipset_data *data, enum ipset_opt opt, const void *value)
|
||||
{
|
||||
assert(data);
|
||||
assert(opt != IPSET_OPT_NONE);
|
||||
assert(value);
|
||||
|
||||
switch (opt) {
|
||||
/* Common ones */
|
||||
case IPSET_SETNAME:
|
||||
ipset_strlcpy(data->setname, value, IPSET_MAXNAMELEN);
|
||||
break;
|
||||
case IPSET_OPT_TYPE:
|
||||
data->type = value;
|
||||
break;
|
||||
case IPSET_OPT_FAMILY:
|
||||
data->family = *(const uint8_t *) value;
|
||||
D("family set to %u", data->family);
|
||||
break;
|
||||
/* CADT options */
|
||||
case IPSET_OPT_IP:
|
||||
if (!(data->family == AF_INET || data->family == AF_INET6))
|
||||
return -1;
|
||||
copy_addr(data->family, &data->ip, value);
|
||||
break;
|
||||
case IPSET_OPT_IP_TO:
|
||||
if (!(data->family == AF_INET || data->family == AF_INET6))
|
||||
return -1;
|
||||
copy_addr(data->family, &data->ip_to, value);
|
||||
break;
|
||||
case IPSET_OPT_CIDR:
|
||||
data->cidr = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_PORT:
|
||||
data->port = *(const uint16_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_PORT_TO:
|
||||
data->port_to = *(const uint16_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_TIMEOUT:
|
||||
data->timeout = *(const uint32_t *) value;
|
||||
break;
|
||||
/* Create-specific options */
|
||||
case IPSET_OPT_GC:
|
||||
data->create.gc = *(const uint32_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_HASHSIZE:
|
||||
data->create.hashsize = *(const uint32_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_MAXELEM:
|
||||
data->create.maxelem = *(const uint32_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_NETMASK:
|
||||
data->create.netmask = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_PROBES:
|
||||
data->create.probes = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_RESIZE:
|
||||
data->create.resize = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_SIZE:
|
||||
data->create.size = *(const uint32_t *) value;
|
||||
break;
|
||||
/* Create-specific options, filled out by the kernel */
|
||||
case IPSET_OPT_ELEMENTS:
|
||||
data->create.elements = *(const uint32_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_REFERENCES:
|
||||
data->create.references = *(const uint32_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_MEMSIZE:
|
||||
data->create.memsize = *(const uint32_t *) value;
|
||||
break;
|
||||
/* Create-specific options, type */
|
||||
case IPSET_OPT_TYPENAME:
|
||||
ipset_strlcpy(data->create.typename, value,
|
||||
IPSET_MAXNAMELEN);
|
||||
break;
|
||||
case IPSET_OPT_REVISION:
|
||||
data->create.revision = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_REVISION_MIN:
|
||||
data->create.revision_min = *(const uint8_t *) value;
|
||||
break;
|
||||
/* ADT-specific options */
|
||||
case IPSET_OPT_ETHER:
|
||||
memcpy(data->adt.ether, value, ETH_ALEN);
|
||||
break;
|
||||
case IPSET_OPT_NAME:
|
||||
ipset_strlcpy(data->adt.name, value, IPSET_MAXNAMELEN);
|
||||
break;
|
||||
case IPSET_OPT_NAMEREF:
|
||||
ipset_strlcpy(data->adt.nameref, value, IPSET_MAXNAMELEN);
|
||||
break;
|
||||
case IPSET_OPT_IP2:
|
||||
if (!(data->family == AF_INET || data->family == AF_INET6))
|
||||
return -1;
|
||||
copy_addr(data->family, &data->adt.ip2, value);
|
||||
break;
|
||||
case IPSET_OPT_CIDR2:
|
||||
data->adt.cidr2 = *(const uint8_t *) value;
|
||||
break;
|
||||
case IPSET_OPT_PROTO:
|
||||
data->adt.proto = *(const uint8_t *) value;
|
||||
break;
|
||||
/* Swap/rename */
|
||||
case IPSET_OPT_SETNAME2:
|
||||
ipset_strlcpy(data->setname2, value, IPSET_MAXNAMELEN);
|
||||
break;
|
||||
/* flags */
|
||||
case IPSET_OPT_EXIST:
|
||||
flag_type_attr(data, opt, IPSET_FLAG_EXIST);
|
||||
break;
|
||||
case IPSET_OPT_BEFORE:
|
||||
cadt_flag_type_attr(data, opt, IPSET_FLAG_BEFORE);
|
||||
break;
|
||||
case IPSET_OPT_FLAGS:
|
||||
data->flags = *(const uint32_t *)value;
|
||||
break;
|
||||
case IPSET_OPT_CADT_FLAGS:
|
||||
data->cadt_flags = *(const uint32_t *)value;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
};
|
||||
|
||||
ipset_data_flags_set(data, IPSET_FLAG(opt));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_get - get data from the data blob
|
||||
* @data: data blob
|
||||
* @opt: option kind of the requested data
|
||||
*
|
||||
* Returns the pointer to the requested kind of data from the data blob
|
||||
* if it is set. If the option kind is not set or is an unkown type,
|
||||
* NULL is returned.
|
||||
*/
|
||||
const void *
|
||||
ipset_data_get(const struct ipset_data *data, enum ipset_opt opt)
|
||||
{
|
||||
assert(data);
|
||||
assert(opt != IPSET_OPT_NONE);
|
||||
|
||||
if (!(opt == IPSET_OPT_TYPENAME || ipset_data_test(data, opt)))
|
||||
return NULL;
|
||||
|
||||
switch (opt) {
|
||||
/* Common ones */
|
||||
case IPSET_SETNAME:
|
||||
return data->setname;
|
||||
case IPSET_OPT_TYPE:
|
||||
return data->type;
|
||||
case IPSET_OPT_TYPENAME:
|
||||
if (ipset_data_test(data, IPSET_OPT_TYPE))
|
||||
return data->type->name;
|
||||
else if (ipset_data_test(data, IPSET_OPT_TYPENAME))
|
||||
return data->create.typename;
|
||||
return NULL;
|
||||
case IPSET_OPT_FAMILY:
|
||||
return &data->family;
|
||||
/* CADT options */
|
||||
case IPSET_OPT_IP:
|
||||
return &data->ip;
|
||||
case IPSET_OPT_IP_TO:
|
||||
return &data->ip_to;
|
||||
case IPSET_OPT_CIDR:
|
||||
return &data->cidr;
|
||||
case IPSET_OPT_PORT:
|
||||
return &data->port;
|
||||
case IPSET_OPT_PORT_TO:
|
||||
return &data->port_to;
|
||||
case IPSET_OPT_TIMEOUT:
|
||||
return &data->timeout;
|
||||
/* Create-specific options */
|
||||
case IPSET_OPT_GC:
|
||||
return &data->create.gc;
|
||||
case IPSET_OPT_HASHSIZE:
|
||||
return &data->create.hashsize;
|
||||
case IPSET_OPT_MAXELEM:
|
||||
return &data->create.maxelem;
|
||||
case IPSET_OPT_NETMASK:
|
||||
return &data->create.netmask;
|
||||
case IPSET_OPT_PROBES:
|
||||
return &data->create.probes;
|
||||
case IPSET_OPT_RESIZE:
|
||||
return &data->create.resize;
|
||||
case IPSET_OPT_SIZE:
|
||||
return &data->create.size;
|
||||
/* Create-specific options, filled out by the kernel */
|
||||
case IPSET_OPT_ELEMENTS:
|
||||
return &data->create.elements;
|
||||
case IPSET_OPT_REFERENCES:
|
||||
return &data->create.references;
|
||||
case IPSET_OPT_MEMSIZE:
|
||||
return &data->create.memsize;
|
||||
/* Create-specific options, TYPE */
|
||||
case IPSET_OPT_REVISION:
|
||||
return &data->create.revision;
|
||||
case IPSET_OPT_REVISION_MIN:
|
||||
return &data->create.revision_min;
|
||||
/* ADT-specific options */
|
||||
case IPSET_OPT_ETHER:
|
||||
return data->adt.ether;
|
||||
case IPSET_OPT_NAME:
|
||||
return data->adt.name;
|
||||
case IPSET_OPT_NAMEREF:
|
||||
return data->adt.nameref;
|
||||
case IPSET_OPT_IP2:
|
||||
return &data->adt.ip2;
|
||||
case IPSET_OPT_CIDR2:
|
||||
return &data->adt.cidr2;
|
||||
case IPSET_OPT_PROTO:
|
||||
return &data->adt.proto;
|
||||
/* Swap/rename */
|
||||
case IPSET_OPT_SETNAME2:
|
||||
return data->setname2;
|
||||
/* flags */
|
||||
case IPSET_OPT_FLAGS:
|
||||
case IPSET_OPT_EXIST:
|
||||
return &data->flags;
|
||||
case IPSET_OPT_CADT_FLAGS:
|
||||
case IPSET_OPT_BEFORE:
|
||||
return &data->cadt_flags;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_sizeof - calculates the size of the data type
|
||||
* @opt: option kind of the data
|
||||
* @family: INET family
|
||||
*
|
||||
* Returns the size required to store the given data type.
|
||||
*/
|
||||
size_t
|
||||
ipset_data_sizeof(enum ipset_opt opt, uint8_t family)
|
||||
{
|
||||
assert(opt != IPSET_OPT_NONE);
|
||||
|
||||
switch (opt) {
|
||||
case IPSET_OPT_IP:
|
||||
case IPSET_OPT_IP_TO:
|
||||
case IPSET_OPT_IP2:
|
||||
return family == AF_INET ? sizeof(uint32_t)
|
||||
: sizeof(struct in6_addr);
|
||||
case IPSET_OPT_PORT:
|
||||
case IPSET_OPT_PORT_TO:
|
||||
return sizeof(uint16_t);
|
||||
case IPSET_SETNAME:
|
||||
case IPSET_OPT_NAME:
|
||||
case IPSET_OPT_NAMEREF:
|
||||
return IPSET_MAXNAMELEN;
|
||||
case IPSET_OPT_TIMEOUT:
|
||||
case IPSET_OPT_GC:
|
||||
case IPSET_OPT_HASHSIZE:
|
||||
case IPSET_OPT_MAXELEM:
|
||||
case IPSET_OPT_SIZE:
|
||||
case IPSET_OPT_ELEMENTS:
|
||||
case IPSET_OPT_REFERENCES:
|
||||
case IPSET_OPT_MEMSIZE:
|
||||
return sizeof(uint32_t);
|
||||
case IPSET_OPT_CIDR:
|
||||
case IPSET_OPT_CIDR2:
|
||||
case IPSET_OPT_NETMASK:
|
||||
case IPSET_OPT_PROBES:
|
||||
case IPSET_OPT_RESIZE:
|
||||
case IPSET_OPT_PROTO:
|
||||
return sizeof(uint8_t);
|
||||
case IPSET_OPT_ETHER:
|
||||
return ETH_ALEN;
|
||||
/* Flags counted once */
|
||||
case IPSET_OPT_BEFORE:
|
||||
return sizeof(uint32_t);
|
||||
default:
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_setname - return the name of the set from the data blob
|
||||
* @data: data blob
|
||||
*
|
||||
* Return the name of the set from the data blob or NULL if the
|
||||
* name not set yet.
|
||||
*/
|
||||
const char *
|
||||
ipset_data_setname(const struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
return ipset_data_test(data, IPSET_SETNAME) ? data->setname : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_family - return the INET family of the set from the data blob
|
||||
* @data: data blob
|
||||
*
|
||||
* Return the INET family supported by the set from the data blob.
|
||||
* If the family is not set yet, AF_UNSPEC is returned.
|
||||
*/
|
||||
uint8_t
|
||||
ipset_data_family(const struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
return ipset_data_test(data, IPSET_OPT_FAMILY)
|
||||
? data->family : AF_UNSPEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_cidr - return the value of IPSET_OPT_CIDR
|
||||
* @data: data blob
|
||||
*
|
||||
* Return the value of IPSET_OPT_CIDR stored in the data blob.
|
||||
* If it is not set, then the returned value corresponds to
|
||||
* the default one according to the family type or zero.
|
||||
*/
|
||||
uint8_t
|
||||
ipset_data_cidr(const struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
return ipset_data_test(data, IPSET_OPT_CIDR) ? data->cidr :
|
||||
data->family == AF_INET ? 32 :
|
||||
data->family == AF_INET6 ? 128 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_flags - return which fields are set in the data blob
|
||||
* @data: data blob
|
||||
*
|
||||
* Returns the value of the bit field which elements are set.
|
||||
*/
|
||||
uint64_t
|
||||
ipset_data_flags(const struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
return data->bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_reset - reset the data blob to unset
|
||||
* @data: data blob
|
||||
*
|
||||
* Resets the data blob to the unset state for every field.
|
||||
*/
|
||||
void
|
||||
ipset_data_reset(struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_init - create a new data blob
|
||||
*
|
||||
* Return the new data blob initialized to empty. In case of
|
||||
* an error, NULL is retured.
|
||||
*/
|
||||
struct ipset_data *
|
||||
ipset_data_init(void)
|
||||
{
|
||||
return calloc(1, sizeof(struct ipset_data));
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_data_fini - release a data blob created by ipset_data_init
|
||||
*
|
||||
* Release the data blob created by ipset_data_init previously.
|
||||
*/
|
||||
void
|
||||
ipset_data_fini(struct ipset_data *data)
|
||||
{
|
||||
assert(data);
|
||||
free(data);
|
||||
}
|
79
extensions/ipset-5/libipset/icmp.c
Normal file
79
extensions/ipset-5/libipset/icmp.c
Normal file
@@ -0,0 +1,79 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <libipset/utils.h> /* STRNEQ */
|
||||
#include <libipset/icmp.h> /* prototypes */
|
||||
|
||||
struct icmp_names {
|
||||
const char *name;
|
||||
uint8_t type, code;
|
||||
};
|
||||
|
||||
static const struct icmp_names icmp_typecodes[] = {
|
||||
{ "echo-reply", 0, 0 },
|
||||
{ "pong", 0, 0 },
|
||||
{ "network-unreachable", 3, 0 },
|
||||
{ "host-unreachable", 3, 1 },
|
||||
{ "protocol-unreachable", 3, 2 },
|
||||
{ "port-unreachable", 3, 3 },
|
||||
{ "fragmentation-needed", 3, 4 },
|
||||
{ "source-route-failed", 3, 5 },
|
||||
{ "network-unknown", 3, 6 },
|
||||
{ "host-unknown", 3, 7 },
|
||||
{ "network-prohibited", 3, 9 },
|
||||
{ "host-prohibited", 3, 10 },
|
||||
{ "TOS-network-unreachable", 3, 11 },
|
||||
{ "TOS-host-unreachable", 3, 12 },
|
||||
{ "communication-prohibited", 3, 13 },
|
||||
{ "host-precedence-violation", 3, 14 },
|
||||
{ "precedence-cutoff", 3, 15 },
|
||||
{ "source-quench", 4, 0 },
|
||||
{ "network-redirect", 5, 0 },
|
||||
{ "host-redirect", 5, 1 },
|
||||
{ "TOS-network-redirect", 5, 2 },
|
||||
{ "TOS-host-redirect", 5, 3 },
|
||||
{ "echo-request", 8, 0 },
|
||||
{ "ping", 8, 0 },
|
||||
{ "router-advertisement", 9, 0 },
|
||||
{ "router-solicitation", 10, 0 },
|
||||
{ "ttl-zero-during-transit", 11, 0 },
|
||||
{ "ttl-zero-during-reassembly", 11, 1 },
|
||||
{ "ip-header-bad", 12, 0 },
|
||||
{ "required-option-missing", 12, 1 },
|
||||
{ "timestamp-request", 13, 0 },
|
||||
{ "timestamp-reply", 14, 0 },
|
||||
{ "address-mask-request", 17, 0 },
|
||||
{ "address-mask-reply", 18, 0 },
|
||||
};
|
||||
|
||||
const char * id_to_icmp(uint8_t id)
|
||||
{
|
||||
return id < ARRAY_SIZE(icmp_typecodes) ? icmp_typecodes[id].name : NULL;
|
||||
}
|
||||
|
||||
const char * icmp_to_name(uint8_t type, uint8_t code)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
|
||||
if (icmp_typecodes[i].type == type && icmp_typecodes[i].code == code)
|
||||
return icmp_typecodes[i].name;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int name_to_icmp(const char *str, uint16_t *typecode)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(icmp_typecodes); i++)
|
||||
if (STRNCASEQ(icmp_typecodes[i].name, str, strlen(str))) {
|
||||
*typecode = (icmp_typecodes[i].type << 8) | icmp_typecodes[i].code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
66
extensions/ipset-5/libipset/icmpv6.c
Normal file
66
extensions/ipset-5/libipset/icmpv6.c
Normal file
@@ -0,0 +1,66 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <libipset/utils.h> /* STRNEQ */
|
||||
#include <libipset/icmpv6.h> /* prototypes */
|
||||
|
||||
struct icmpv6_names {
|
||||
const char *name;
|
||||
uint8_t type, code;
|
||||
};
|
||||
|
||||
static const struct icmpv6_names icmpv6_typecodes[] = {
|
||||
{ "no-route", 1, 0 },
|
||||
{ "communication-prohibited", 1, 1 },
|
||||
{ "address-unreachable", 1, 3 },
|
||||
{ "port-unreachable", 1, 4 },
|
||||
{ "packet-too-big", 2, 0 },
|
||||
{ "ttl-zero-during-transit", 3, 0 },
|
||||
{ "ttl-zero-during-reassembly", 3, 1 },
|
||||
{ "bad-header", 4, 0 },
|
||||
{ "unknown-header-type", 4, 1 },
|
||||
{ "unknown-option", 4, 2 },
|
||||
{ "echo-request", 128, 0 },
|
||||
{ "ping", 128, 0 },
|
||||
{ "echo-reply", 129, 0 },
|
||||
{ "pong", 129, 0 },
|
||||
{ "router-solicitation", 133, 0 },
|
||||
{ "router-advertisement", 134, 0 },
|
||||
{ "neighbour-solicitation", 135, 0 },
|
||||
{ "neigbour-solicitation", 135, 0 },
|
||||
{ "neighbour-advertisement", 136, 0 },
|
||||
{ "neigbour-advertisement", 136, 0 },
|
||||
{ "redirect", 137, 0 },
|
||||
};
|
||||
|
||||
const char * id_to_icmpv6(uint8_t id)
|
||||
{
|
||||
return id < ARRAY_SIZE(icmpv6_typecodes) ? icmpv6_typecodes[id].name : NULL;
|
||||
}
|
||||
|
||||
const char * icmpv6_to_name(uint8_t type, uint8_t code)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
|
||||
if (icmpv6_typecodes[i].type == type && icmpv6_typecodes[i].code == code)
|
||||
return icmpv6_typecodes[i].name;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int name_to_icmpv6(const char *str, uint16_t *typecode)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(icmpv6_typecodes); i++)
|
||||
if (STRNCASEQ(icmpv6_typecodes[i].name, str, strlen(str))) {
|
||||
*typecode = (icmpv6_typecodes[i].type << 8) | icmpv6_typecodes[i].code;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
258
extensions/ipset-5/libipset/mnl.c
Normal file
258
extensions/ipset-5/libipset/mnl.c
Normal file
@@ -0,0 +1,258 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <assert.h> /* assert */
|
||||
#include <errno.h> /* errno */
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> /* calloc, free */
|
||||
#include <time.h> /* time */
|
||||
#include <arpa/inet.h> /* hto* */
|
||||
|
||||
#include <libipset/linux_ip_set.h> /* enum ipset_cmd */
|
||||
#include <libipset/debug.h> /* D() */
|
||||
#include <libipset/session.h> /* ipset_session_handle */
|
||||
#include <libipset/ui.h> /* IPSET_ENV_EXIST */
|
||||
#include <libipset/utils.h> /* UNUSED */
|
||||
#include <libipset/mnl.h> /* prototypes */
|
||||
|
||||
#include <libmnl/libmnl.h>
|
||||
#include <linux/genetlink.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#ifndef NFNL_SUBSYS_IPSET
|
||||
#define NFNL_SUBSYS_IPSET 6
|
||||
#endif
|
||||
|
||||
/* Internal data structure for the kernel-userspace communication parameters */
|
||||
struct ipset_handle {
|
||||
struct mnl_socket *h; /* the mnl socket */
|
||||
unsigned int seq; /* netlink message sequence number */
|
||||
unsigned int portid; /* the socket port identifier */
|
||||
mnl_cb_t *cb_ctl; /* control block callbacks */
|
||||
void *data; /* data pointer */
|
||||
unsigned int genl_id; /* genetlink ID of ip_set */
|
||||
};
|
||||
|
||||
/* Netlink flags of the commands */
|
||||
static const uint16_t cmdflags[] = {
|
||||
[IPSET_CMD_CREATE-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_CREATE|NLM_F_EXCL,
|
||||
[IPSET_CMD_DESTROY-1] = NLM_F_REQUEST|NLM_F_ACK,
|
||||
[IPSET_CMD_FLUSH-1] = NLM_F_REQUEST|NLM_F_ACK,
|
||||
[IPSET_CMD_RENAME-1] = NLM_F_REQUEST|NLM_F_ACK,
|
||||
[IPSET_CMD_SWAP-1] = NLM_F_REQUEST|NLM_F_ACK,
|
||||
[IPSET_CMD_LIST-1] = NLM_F_REQUEST,
|
||||
[IPSET_CMD_SAVE-1] = NLM_F_REQUEST,
|
||||
[IPSET_CMD_ADD-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
|
||||
[IPSET_CMD_DEL-1] = NLM_F_REQUEST|NLM_F_ACK|NLM_F_EXCL,
|
||||
[IPSET_CMD_TEST-1] = NLM_F_REQUEST|NLM_F_ACK,
|
||||
[IPSET_CMD_HEADER-1] = NLM_F_REQUEST,
|
||||
[IPSET_CMD_TYPE-1] = NLM_F_REQUEST,
|
||||
[IPSET_CMD_PROTOCOL-1] = NLM_F_REQUEST,
|
||||
};
|
||||
|
||||
/**
|
||||
* ipset_get_nlmsg_type - get ipset netlink message type
|
||||
* @nlh: pointer to the netlink message header
|
||||
*
|
||||
* Returns the ipset netlink message type, i.e. the ipset command.
|
||||
*/
|
||||
int
|
||||
ipset_get_nlmsg_type(const struct nlmsghdr *nlh)
|
||||
{
|
||||
const struct genlmsghdr *ghdr = mnl_nlmsg_get_payload(nlh);
|
||||
|
||||
return ghdr->cmd;
|
||||
}
|
||||
|
||||
static void
|
||||
ipset_mnl_fill_hdr(struct ipset_handle *handle, enum ipset_cmd cmd,
|
||||
void *buffer, size_t len UNUSED, uint8_t envflags)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct genlmsghdr *ghdr;
|
||||
|
||||
assert(handle);
|
||||
assert(buffer);
|
||||
assert(cmd > IPSET_CMD_NONE && cmd < IPSET_MSG_MAX);
|
||||
|
||||
nlh = mnl_nlmsg_put_header(buffer);
|
||||
nlh->nlmsg_type = handle->genl_id;
|
||||
nlh->nlmsg_flags = NLM_F_REQUEST;
|
||||
if (cmdflags[cmd-1] & NLM_F_ACK)
|
||||
nlh->nlmsg_flags |= NLM_F_ACK;
|
||||
nlh->nlmsg_seq = handle->seq = time(NULL);
|
||||
|
||||
ghdr = mnl_nlmsg_put_extra_header(nlh, sizeof(struct genlmsghdr));
|
||||
ghdr->cmd = cmd;
|
||||
/* (ge)netlink is dumb, NLM_F_CREATE and NLM_F_DUMP overlap, get misinterpreted. */
|
||||
ghdr->reserved = cmdflags[cmd-1];
|
||||
if (envflags & IPSET_ENV_EXIST)
|
||||
ghdr->reserved &= ~NLM_F_EXCL;
|
||||
}
|
||||
|
||||
static int
|
||||
ipset_mnl_query(struct ipset_handle *handle, void *buffer, size_t len)
|
||||
{
|
||||
struct nlmsghdr *nlh = buffer;
|
||||
int ret;
|
||||
|
||||
assert(handle);
|
||||
assert(buffer);
|
||||
|
||||
if (mnl_socket_sendto(handle->h, nlh, nlh->nlmsg_len) < 0)
|
||||
return -ECOMM;
|
||||
|
||||
D("message sent");
|
||||
ret = mnl_socket_recvfrom(handle->h, buffer, len);
|
||||
D("message received, ret: %d", ret);
|
||||
while (ret > 0) {
|
||||
ret = mnl_cb_run2(buffer, ret,
|
||||
handle->seq, handle->portid,
|
||||
handle->cb_ctl[NLMSG_MIN_TYPE],
|
||||
handle->data,
|
||||
handle->cb_ctl, NLMSG_MIN_TYPE);
|
||||
D("nfln_cb_run2, ret: %d", ret);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
ret = mnl_socket_recvfrom(handle->h, buffer, len);
|
||||
D("message received, ret: %d", ret);
|
||||
}
|
||||
return ret > 0 ? 0 : ret;
|
||||
}
|
||||
|
||||
static int ipset_mnl_getid_acb(const struct nlattr *attr, void *datap)
|
||||
{
|
||||
const struct nlattr **tb = datap;
|
||||
uint16_t type = mnl_attr_get_type(attr);
|
||||
|
||||
if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
|
||||
return MNL_CB_OK;
|
||||
tb[type] = attr;
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
static int ipset_mnl_getid_cb(const struct nlmsghdr *nlhdr, void *datap)
|
||||
{
|
||||
struct ipset_handle *h = datap;
|
||||
const struct nlattr *tb[CTRL_ATTR_MAX+1] = {0};
|
||||
const struct genlmsghdr *ghdr = mnl_nlmsg_get_payload(nlhdr);
|
||||
int ret;
|
||||
|
||||
ret = mnl_attr_parse(nlhdr, sizeof(*ghdr), ipset_mnl_getid_acb, tb);
|
||||
if (ret != MNL_CB_OK)
|
||||
return ret;
|
||||
if (tb[CTRL_ATTR_FAMILY_ID] != NULL)
|
||||
h->genl_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]);
|
||||
return MNL_CB_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look up the GENL identifier for the ip_set subsystem, and store it in
|
||||
* @h->genl_id. On success, 0 is returned, otherwise error encoded as
|
||||
* negative number.
|
||||
*/
|
||||
static int ipset_mnl_getid(struct ipset_handle *h, bool modprobe)
|
||||
{
|
||||
size_t buf_size = 8192; //MNL_SOCKET_BUFFER_SIZE;
|
||||
struct nlmsghdr *nlhdr;
|
||||
struct genlmsghdr *ghdr;
|
||||
char *buf;
|
||||
int ret = -ENOENT;
|
||||
|
||||
h->genl_id = 0;
|
||||
|
||||
if (modprobe) {
|
||||
/* genetlink has no autoloading like nfnetlink... */
|
||||
system("/sbin/modprobe -q ip_set");
|
||||
}
|
||||
|
||||
buf = malloc(buf_size);
|
||||
if (buf == NULL)
|
||||
return -errno;
|
||||
|
||||
nlhdr = mnl_nlmsg_put_header(buf);
|
||||
nlhdr->nlmsg_type = GENL_ID_CTRL;
|
||||
nlhdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
|
||||
ghdr = mnl_nlmsg_put_extra_header(nlhdr, sizeof(struct genlmsghdr));
|
||||
ghdr->cmd = CTRL_CMD_GETFAMILY;
|
||||
ghdr->version = 2;
|
||||
if (!mnl_attr_put_strz_check(nlhdr, buf_size,
|
||||
CTRL_ATTR_FAMILY_NAME, "ip_set"))
|
||||
goto out;
|
||||
|
||||
ret = mnl_socket_sendto(h->h, buf, nlhdr->nlmsg_len);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = mnl_socket_recvfrom(h->h, buf, buf_size);
|
||||
while (ret > 0) {
|
||||
ret = mnl_cb_run(buf, ret, 0, 0, ipset_mnl_getid_cb, h);
|
||||
if (ret <= 0)
|
||||
break;
|
||||
ret = mnl_socket_recvfrom(h->h, buf, buf_size);
|
||||
}
|
||||
if (h->genl_id == 0 && !modprobe)
|
||||
/* Redo, with modprobe this time. */
|
||||
ret = ipset_mnl_getid(h, true);
|
||||
if (h->genl_id > 0)
|
||||
ret = 0;
|
||||
out:
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct ipset_handle *
|
||||
ipset_mnl_init(mnl_cb_t *cb_ctl, void *data)
|
||||
{
|
||||
struct ipset_handle *handle;
|
||||
|
||||
assert(cb_ctl);
|
||||
assert(data);
|
||||
|
||||
handle = calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
handle->h = mnl_socket_open(NETLINK_GENERIC);
|
||||
if (!handle->h)
|
||||
goto free_handle;
|
||||
|
||||
if (mnl_socket_bind(handle->h, 0, MNL_SOCKET_AUTOPID) < 0)
|
||||
goto close_nl;
|
||||
|
||||
handle->portid = mnl_socket_get_portid(handle->h);
|
||||
handle->cb_ctl = cb_ctl;
|
||||
handle->data = data;
|
||||
|
||||
if (ipset_mnl_getid(handle, false) < 0)
|
||||
goto close_nl;
|
||||
return handle;
|
||||
|
||||
close_nl:
|
||||
mnl_socket_close(handle->h);
|
||||
free_handle:
|
||||
free(handle);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
ipset_mnl_fini(struct ipset_handle *handle)
|
||||
{
|
||||
assert(handle);
|
||||
|
||||
if (handle->h)
|
||||
mnl_socket_close(handle->h);
|
||||
|
||||
free(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct ipset_transport ipset_mnl_transport = {
|
||||
.init = ipset_mnl_init,
|
||||
.fini = ipset_mnl_fini,
|
||||
.fill_hdr = ipset_mnl_fill_hdr,
|
||||
.query = ipset_mnl_query,
|
||||
};
|
1526
extensions/ipset-5/libipset/parse.c
Normal file
1526
extensions/ipset-5/libipset/parse.c
Normal file
File diff suppressed because it is too large
Load Diff
743
extensions/ipset-5/libipset/print.c
Normal file
743
extensions/ipset-5/libipset/print.c
Normal file
@@ -0,0 +1,743 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <assert.h> /* assert */
|
||||
#include <errno.h> /* errno */
|
||||
#include <stdio.h> /* snprintf */
|
||||
#include <netdb.h> /* getservbyport */
|
||||
#include <sys/types.h> /* inet_ntop */
|
||||
#include <sys/socket.h> /* inet_ntop */
|
||||
#include <arpa/inet.h> /* inet_ntop */
|
||||
#include <net/ethernet.h> /* ETH_ALEN */
|
||||
|
||||
#include <libipset/debug.h> /* D() */
|
||||
#include <libipset/data.h> /* ipset_data_* */
|
||||
#include <libipset/icmp.h> /* icmp_to_name */
|
||||
#include <libipset/icmpv6.h> /* icmpv6_to_name */
|
||||
#include <libipset/parse.h> /* IPSET_*_SEPARATOR */
|
||||
#include <libipset/types.h> /* ipset set types */
|
||||
#include <libipset/session.h> /* IPSET_FLAG_ */
|
||||
#include <libipset/utils.h> /* UNUSED */
|
||||
#include <libipset/ui.h> /* IPSET_ENV_* */
|
||||
#include <libipset/print.h> /* prototypes */
|
||||
|
||||
/* Print data (to output buffer). All function must follow snprintf. */
|
||||
|
||||
#define SNPRINTF_FAILURE(size, len, offset) \
|
||||
do { \
|
||||
if (size < 0 || (unsigned int) size >= len) \
|
||||
return size; \
|
||||
offset += size; \
|
||||
len -= size; \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* ipset_print_ether - print ethernet address to string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print Ethernet address to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_ether(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const unsigned char *ether;
|
||||
int i, size, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_ETHER);
|
||||
|
||||
if (len < ETH_ALEN*3)
|
||||
return -1;
|
||||
|
||||
ether = ipset_data_get(data, opt);
|
||||
assert(ether);
|
||||
|
||||
size = snprintf(buf, len, "%02X", ether[0]);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
for (i = 1; i < ETH_ALEN; i++) {
|
||||
size = snprintf(buf + offset, len, ":%02X", ether[i]);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_family - print INET family
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print INET family string to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_family(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
uint8_t family;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_FAMILY);
|
||||
|
||||
if (len < strlen("inet6") + 1)
|
||||
return -1;
|
||||
|
||||
family = ipset_data_family(data);
|
||||
|
||||
return snprintf(buf, len, "%s",
|
||||
family == AF_INET ? "inet" :
|
||||
family == AF_INET6 ? "inet6" : "any");
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_type - print ipset type string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print ipset module string identifier to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_type(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const struct ipset_type *type;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_TYPE);
|
||||
|
||||
type = ipset_data_get(data, opt);
|
||||
assert(type);
|
||||
if (len < strlen(type->name) + 1)
|
||||
return -1;
|
||||
|
||||
return snprintf(buf, len, "%s", type->name);
|
||||
}
|
||||
|
||||
#define GETNAMEINFO(family, f, n) \
|
||||
static inline int \
|
||||
__getnameinfo##f(char *buf, unsigned int len, \
|
||||
int flags, const union nf_inet_addr *addr) \
|
||||
{ \
|
||||
struct sockaddr_in##n saddr; \
|
||||
int err; \
|
||||
\
|
||||
memset(&saddr, 0, sizeof(saddr)); \
|
||||
in##f##cpy(&saddr.sin##n##_addr, &addr->in##n); \
|
||||
saddr.sin##n##_family = family; \
|
||||
\
|
||||
err = getnameinfo((const struct sockaddr *)&saddr, \
|
||||
sizeof(saddr), \
|
||||
buf, len, NULL, 0, flags); \
|
||||
\
|
||||
if (err == EAI_AGAIN && !(flags & NI_NUMERICHOST)) \
|
||||
err = getnameinfo((const struct sockaddr *)&saddr, \
|
||||
sizeof(saddr), \
|
||||
buf, len, NULL, 0, \
|
||||
flags | NI_NUMERICHOST); \
|
||||
D("getnameinfo err: %i, errno %i", err, errno); \
|
||||
return (err == 0 ? (int)strlen(buf) : \
|
||||
(err == EAI_OVERFLOW || err == EAI_SYSTEM) ? (int)len : -1);\
|
||||
}
|
||||
|
||||
#define SNPRINTF_IP(mask, f) \
|
||||
static int \
|
||||
snprintf_ipv##f(char *buf, unsigned int len, int flags, \
|
||||
const union nf_inet_addr *ip, uint8_t cidr) \
|
||||
{ \
|
||||
int size, offset = 0; \
|
||||
\
|
||||
size = __getnameinfo##f(buf, len, flags, ip); \
|
||||
SNPRINTF_FAILURE(size, len, offset); \
|
||||
\
|
||||
D("cidr %u mask %u", cidr, mask); \
|
||||
if (cidr == mask) \
|
||||
return offset; \
|
||||
D("print cidr"); \
|
||||
size = snprintf(buf + offset, len, \
|
||||
"%s%u", IPSET_CIDR_SEPARATOR, cidr); \
|
||||
SNPRINTF_FAILURE(size, len, offset); \
|
||||
return offset; \
|
||||
}
|
||||
|
||||
GETNAMEINFO(AF_INET, 4, )
|
||||
SNPRINTF_IP(32, 4)
|
||||
|
||||
GETNAMEINFO(AF_INET6, 6, 6)
|
||||
SNPRINTF_IP(128, 6)
|
||||
|
||||
/**
|
||||
* ipset_print_ip - print IPv4|IPv6 address to string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print IPv4|IPv6 address, address/cidr or address range to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_ip(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env)
|
||||
{
|
||||
const union nf_inet_addr *ip;
|
||||
uint8_t family, cidr;
|
||||
int flags, size, offset = 0;
|
||||
enum ipset_opt cidropt;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_IP || opt == IPSET_OPT_IP2);
|
||||
|
||||
D("len: %u", len);
|
||||
family = ipset_data_family(data);
|
||||
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
|
||||
if (ipset_data_test(data, cidropt)) {
|
||||
cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
|
||||
D("CIDR: %u", cidr);
|
||||
} else
|
||||
cidr = family == AF_INET6 ? 128 : 32;
|
||||
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
|
||||
|
||||
ip = ipset_data_get(data, opt);
|
||||
assert(ip);
|
||||
if (family == AF_INET)
|
||||
size = snprintf_ipv4(buf, len, flags, ip, cidr);
|
||||
else if (family == AF_INET6)
|
||||
size = snprintf_ipv6(buf, len, flags, ip, cidr);
|
||||
else
|
||||
return -1;
|
||||
D("size %i, len %u", size, len);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
D("len: %u, offset %u", len, offset);
|
||||
if (!ipset_data_test(data, IPSET_OPT_IP_TO))
|
||||
return offset;
|
||||
|
||||
size = snprintf(buf + offset, len, "%s", IPSET_RANGE_SEPARATOR);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
ip = ipset_data_get(data, IPSET_OPT_IP_TO);
|
||||
if (family == AF_INET)
|
||||
size = snprintf_ipv4(buf + offset, len, flags, ip, cidr);
|
||||
else if (family == AF_INET6)
|
||||
size = snprintf_ipv6(buf + offset, len, flags, ip, cidr);
|
||||
else
|
||||
return -1;
|
||||
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_ipaddr - print IPv4|IPv6 address to string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print IPv4|IPv6 address or address/cidr to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_ipaddr(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env)
|
||||
{
|
||||
const union nf_inet_addr *ip;
|
||||
uint8_t family, cidr;
|
||||
enum ipset_opt cidropt;
|
||||
int flags;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_IP
|
||||
|| opt == IPSET_OPT_IP_TO
|
||||
|| opt == IPSET_OPT_IP2);
|
||||
|
||||
family = ipset_data_family(data);
|
||||
cidropt = opt == IPSET_OPT_IP ? IPSET_OPT_CIDR : IPSET_OPT_CIDR2;
|
||||
if (ipset_data_test(data, cidropt))
|
||||
cidr = *(const uint8_t *) ipset_data_get(data, cidropt);
|
||||
else
|
||||
cidr = family == AF_INET6 ? 128 : 32;
|
||||
flags = env & (1 << IPSET_ENV_RESOLVE) ? 0 : NI_NUMERICHOST;
|
||||
|
||||
ip = ipset_data_get(data, opt);
|
||||
assert(ip);
|
||||
if (family == AF_INET)
|
||||
return snprintf_ipv4(buf, len, flags, ip, cidr);
|
||||
else if (family == AF_INET6)
|
||||
return snprintf_ipv6(buf, len, flags, ip, cidr);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_number - print number to string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print number to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_number(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
size_t maxsize;
|
||||
const void *number;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
|
||||
number = ipset_data_get(data, opt);
|
||||
maxsize = ipset_data_sizeof(opt, AF_INET);
|
||||
D("opt: %u, maxsize %zu", opt, maxsize);
|
||||
if (maxsize == sizeof(uint8_t))
|
||||
return snprintf(buf, len, "%u", *(const uint8_t *) number);
|
||||
else if (maxsize == sizeof(uint16_t))
|
||||
return snprintf(buf, len, "%u", *(const uint16_t *) number);
|
||||
else if (maxsize == sizeof(uint32_t))
|
||||
return snprintf(buf, len, "%lu",
|
||||
(long unsigned) *(const uint32_t *) number);
|
||||
else
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_name - print setname element string
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print setname element string to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_name(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const char *name;
|
||||
int size, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_NAME);
|
||||
|
||||
if (len < 2*IPSET_MAXNAMELEN + 2 + strlen("before"))
|
||||
return -1;
|
||||
|
||||
name = ipset_data_get(data, opt);
|
||||
assert(name);
|
||||
size = snprintf(buf, len, "%s", name);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
if (ipset_data_test(data, IPSET_OPT_NAMEREF)) {
|
||||
bool before = false;
|
||||
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_FLAGS))) {
|
||||
const uint32_t *flags =
|
||||
ipset_data_get(data, IPSET_OPT_FLAGS);
|
||||
before = (*flags) & IPSET_FLAG_BEFORE;
|
||||
}
|
||||
size = snprintf(buf + offset, len,
|
||||
" %s %s", before ? "before" : "after",
|
||||
(const char *) ipset_data_get(data,
|
||||
IPSET_OPT_NAMEREF));
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_port - print port or port range
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print port or port range to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_port(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const uint16_t *port;
|
||||
int size, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_PORT);
|
||||
|
||||
if (len < 2*strlen("65535") + 2)
|
||||
return -1;
|
||||
|
||||
port = ipset_data_get(data, IPSET_OPT_PORT);
|
||||
assert(port);
|
||||
size = snprintf(buf, len, "%u", *port);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
if (ipset_data_test(data, IPSET_OPT_PORT_TO)) {
|
||||
port = ipset_data_get(data, IPSET_OPT_PORT_TO);
|
||||
size = snprintf(buf + offset, len,
|
||||
"%s%u",
|
||||
IPSET_RANGE_SEPARATOR, *port);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_proto - print protocol name
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print protocol name to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_proto(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const struct protoent *protoent;
|
||||
uint8_t proto;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_PROTO);
|
||||
|
||||
proto = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_PROTO);
|
||||
assert(proto);
|
||||
|
||||
protoent = getprotobynumber(proto);
|
||||
if (protoent)
|
||||
return snprintf(buf, len, "%s", protoent->p_name);
|
||||
|
||||
/* Should not happen */
|
||||
return snprintf(buf, len, "%u", proto);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_icmp - print ICMP code name or type/code
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print ICMP code name or type/code name to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_icmp(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const char *name;
|
||||
uint16_t typecode;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_PORT);
|
||||
|
||||
typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
|
||||
name = icmp_to_name(typecode >> 8, typecode & 0xFF);
|
||||
if (name != NULL)
|
||||
return snprintf(buf, len, "%s", name);
|
||||
else
|
||||
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_icmpv6 - print ICMPv6 code name or type/code
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print ICMPv6 code name or type/code name to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_icmpv6(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
const char *name;
|
||||
uint16_t typecode;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_PORT);
|
||||
|
||||
typecode = *(const uint16_t *) ipset_data_get(data, IPSET_OPT_PORT);
|
||||
name = icmpv6_to_name(typecode >> 8, typecode & 0xFF);
|
||||
if (name != NULL)
|
||||
return snprintf(buf, len, "%s", name);
|
||||
else
|
||||
return snprintf(buf, len, "%u/%u", typecode >> 8, typecode & 0xFF);
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_proto_port - print proto:port
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print protocol and port to output buffer.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_proto_port(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env UNUSED)
|
||||
{
|
||||
int size, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
assert(opt == IPSET_OPT_PORT);
|
||||
|
||||
if (ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_PROTO))) {
|
||||
uint8_t proto = *(const uint8_t *) ipset_data_get(data,
|
||||
IPSET_OPT_PROTO);
|
||||
size = ipset_print_proto(buf, len, data, IPSET_OPT_PROTO, env);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
if (len < 2)
|
||||
return -ENOSPC;
|
||||
size = snprintf(buf + offset, len, IPSET_PROTO_SEPARATOR);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
switch (proto) {
|
||||
case IPPROTO_TCP:
|
||||
case IPPROTO_UDP:
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
return ipset_print_icmp(buf + offset, len, data,
|
||||
IPSET_OPT_PORT, env);
|
||||
case IPPROTO_ICMPV6:
|
||||
return ipset_print_icmpv6(buf + offset, len, data,
|
||||
IPSET_OPT_PORT, env);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
size = ipset_print_port(buf + offset, len, data, IPSET_OPT_PORT, env);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
#define print_second(data) \
|
||||
ipset_data_flags_test(data, \
|
||||
IPSET_FLAG(IPSET_OPT_PORT)|IPSET_FLAG(IPSET_OPT_ETHER))
|
||||
|
||||
#define print_third(data) \
|
||||
ipset_data_flags_test(data, IPSET_FLAG(IPSET_OPT_IP2))
|
||||
|
||||
/**
|
||||
* ipset_print_elem - print ADT elem according to settype
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print (multipart) element according to settype
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_elem(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt UNUSED,
|
||||
uint8_t env)
|
||||
{
|
||||
const struct ipset_type *type;
|
||||
int size, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
|
||||
type = ipset_data_get(data, IPSET_OPT_TYPE);
|
||||
if (!type)
|
||||
return -1;
|
||||
|
||||
size = type->elem[IPSET_DIM_ONE].print(buf, len, data,
|
||||
type->elem[IPSET_DIM_ONE].opt, env);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
IF_D(ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt),
|
||||
"print second elem");
|
||||
if (type->dimension == IPSET_DIM_ONE
|
||||
|| (type->last_elem_optional
|
||||
&& !ipset_data_test(data, type->elem[IPSET_DIM_TWO].opt)))
|
||||
return offset;
|
||||
|
||||
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
size = type->elem[IPSET_DIM_TWO].print(buf + offset, len, data,
|
||||
type->elem[IPSET_DIM_TWO].opt, env);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
if (type->dimension == IPSET_DIM_TWO
|
||||
|| (type->last_elem_optional
|
||||
&& !ipset_data_test(data, type->elem[IPSET_DIM_THREE].opt)))
|
||||
return offset;
|
||||
|
||||
size = snprintf(buf + offset, len, IPSET_ELEM_SEPARATOR);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
size = type->elem[IPSET_DIM_THREE].print(buf + offset, len, data,
|
||||
type->elem[IPSET_DIM_THREE].opt, env);
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_flag - print a flag
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Print a flag, i.e. option without value
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_flag(char *buf UNUSED, unsigned int len UNUSED,
|
||||
const struct ipset_data *data UNUSED,
|
||||
enum ipset_opt opt UNUSED, uint8_t env UNUSED)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_print_data - print data, generic fuction
|
||||
* @buf: printing buffer
|
||||
* @len: length of available buffer space
|
||||
* @data: data blob
|
||||
* @opt: the option kind
|
||||
* @env: environment flags
|
||||
*
|
||||
* Generic wrapper of the printing functions.
|
||||
*
|
||||
* Return lenght of printed string or error size.
|
||||
*/
|
||||
int
|
||||
ipset_print_data(char *buf, unsigned int len,
|
||||
const struct ipset_data *data, enum ipset_opt opt,
|
||||
uint8_t env)
|
||||
{
|
||||
int size = 0, offset = 0;
|
||||
|
||||
assert(buf);
|
||||
assert(len > 0);
|
||||
assert(data);
|
||||
|
||||
switch (opt) {
|
||||
case IPSET_OPT_FAMILY:
|
||||
size = ipset_print_family(buf, len, data, opt, env);
|
||||
break;
|
||||
case IPSET_OPT_TYPE:
|
||||
size = ipset_print_type(buf, len, data, opt, env);
|
||||
break;
|
||||
case IPSET_SETNAME:
|
||||
size = snprintf(buf, len, "%s", ipset_data_setname(data));
|
||||
break;
|
||||
case IPSET_OPT_ELEM:
|
||||
size = ipset_print_elem(buf, len, data, opt, env);
|
||||
break;
|
||||
case IPSET_OPT_IP:
|
||||
size = ipset_print_ip(buf, len, data, opt, env);
|
||||
break;
|
||||
case IPSET_OPT_PORT:
|
||||
size = ipset_print_port(buf, len, data, opt, env);
|
||||
break;
|
||||
case IPSET_OPT_GC:
|
||||
case IPSET_OPT_HASHSIZE:
|
||||
case IPSET_OPT_MAXELEM:
|
||||
case IPSET_OPT_NETMASK:
|
||||
case IPSET_OPT_PROBES:
|
||||
case IPSET_OPT_RESIZE:
|
||||
case IPSET_OPT_TIMEOUT:
|
||||
case IPSET_OPT_REFERENCES:
|
||||
case IPSET_OPT_ELEMENTS:
|
||||
case IPSET_OPT_SIZE:
|
||||
size = ipset_print_number(buf, len, data, opt, env);
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
SNPRINTF_FAILURE(size, len, offset);
|
||||
|
||||
return offset;
|
||||
}
|
1915
extensions/ipset-5/libipset/session.c
Normal file
1915
extensions/ipset-5/libipset/session.c
Normal file
File diff suppressed because it is too large
Load Diff
538
extensions/ipset-5/libipset/types.c
Normal file
538
extensions/ipset-5/libipset/types.c
Normal file
@@ -0,0 +1,538 @@
|
||||
/* Copyright 2007-2010 Jozsef Kadlecsik (kadlec@blackhole.kfki.hu)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <assert.h> /* assert */
|
||||
#include <errno.h> /* errno */
|
||||
#include <net/ethernet.h> /* ETH_ALEN */
|
||||
#include <netinet/in.h> /* struct in6_addr */
|
||||
#include <sys/socket.h> /* AF_ */
|
||||
#include <stdlib.h> /* malloc, free */
|
||||
#include <stdio.h> /* FIXME: debug */
|
||||
#include <libmnl/libmnl.h> /* MNL_ALIGN */
|
||||
|
||||
#include <libipset/debug.h> /* D() */
|
||||
#include <libipset/data.h> /* ipset_data_* */
|
||||
#include <libipset/session.h> /* ipset_cmd */
|
||||
#include <libipset/utils.h> /* STREQ */
|
||||
#include <libipset/types.h> /* prototypes */
|
||||
|
||||
/* Userspace cache of sets which exists in the kernel */
|
||||
|
||||
struct ipset {
|
||||
char name[IPSET_MAXNAMELEN]; /* set name */
|
||||
const struct ipset_type *type; /* set type */
|
||||
uint8_t family; /* family */
|
||||
struct ipset *next;
|
||||
};
|
||||
|
||||
static struct ipset_type *typelist = NULL; /* registered set types */
|
||||
static struct ipset *setlist = NULL; /* cached sets */
|
||||
|
||||
/**
|
||||
* ipset_cache_add - add a set to the cache
|
||||
* @name: set name
|
||||
* @type: set type structure
|
||||
*
|
||||
* Add the named set to the internal cache with the specified
|
||||
* set type. The set name must be unique.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_cache_add(const char *name, const struct ipset_type *type,
|
||||
uint8_t family)
|
||||
{
|
||||
struct ipset *s, *n;
|
||||
|
||||
assert(name);
|
||||
assert(type);
|
||||
|
||||
n = malloc(sizeof(*n));
|
||||
if (n == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ipset_strlcpy(n->name, name, IPSET_MAXNAMELEN);
|
||||
n->type = type;
|
||||
n->family = family;
|
||||
n->next = NULL;
|
||||
|
||||
if (setlist == NULL) {
|
||||
setlist = n;
|
||||
return 0;
|
||||
}
|
||||
for (s = setlist; s->next != NULL; s = s->next) {
|
||||
if (STREQ(name, s->name)) {
|
||||
free(n);
|
||||
return -EEXIST;
|
||||
}
|
||||
}
|
||||
s->next = n;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_cache_del - delete set from the cache
|
||||
* @name: set name
|
||||
*
|
||||
* Delete the named set from the internal cache. If NULL is
|
||||
* specified as setname, the whole cache is emptied.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_cache_del(const char *name)
|
||||
{
|
||||
struct ipset *s, *match = NULL, *prev = NULL;
|
||||
|
||||
if (!name) {
|
||||
for (s = setlist; s != NULL; ) {
|
||||
prev = s;
|
||||
s = s->next;
|
||||
free(prev);
|
||||
}
|
||||
setlist = NULL;
|
||||
return 0;
|
||||
}
|
||||
for (s = setlist; s != NULL && match == NULL; s = s->next) {
|
||||
if (STREQ(s->name, name)) {
|
||||
match = s;
|
||||
if (prev == NULL)
|
||||
setlist = match->next;
|
||||
else
|
||||
prev->next = match->next;
|
||||
}
|
||||
prev = s;
|
||||
}
|
||||
if (match == NULL)
|
||||
return -EEXIST;
|
||||
|
||||
free(match);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_cache_rename - rename a set in the cache
|
||||
* @from: the set to rename
|
||||
* @to: the new name of the set
|
||||
*
|
||||
* Rename the given set in the cache.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_cache_rename(const char *from, const char *to)
|
||||
{
|
||||
struct ipset *s;
|
||||
|
||||
assert(from);
|
||||
assert(to);
|
||||
|
||||
for (s = setlist; s != NULL; s = s->next) {
|
||||
if (STREQ(s->name, from)) {
|
||||
ipset_strlcpy(s->name, to, IPSET_MAXNAMELEN);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_cache_swap - swap two sets in the cache
|
||||
* @from: the first set
|
||||
* @to: the second set
|
||||
*
|
||||
* Swap two existing sets in the cache.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_cache_swap(const char *from, const char *to)
|
||||
{
|
||||
struct ipset *s, *a = NULL, *b = NULL;
|
||||
|
||||
assert(from);
|
||||
assert(to);
|
||||
|
||||
for (s = setlist; s != NULL && (a == NULL || b == NULL); s = s->next) {
|
||||
if (a == NULL && STREQ(s->name, from))
|
||||
a = s;
|
||||
if (b == NULL && STREQ(s->name, to))
|
||||
b = s;
|
||||
}
|
||||
if (a != NULL && b != NULL) {
|
||||
ipset_strlcpy(a->name, to, IPSET_MAXNAMELEN);
|
||||
ipset_strlcpy(b->name, from, IPSET_MAXNAMELEN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
#define MATCH_FAMILY(type, f) \
|
||||
(f == AF_UNSPEC || type->family == f || type->family == AF_INET46)
|
||||
|
||||
bool
|
||||
ipset_match_typename(const char *name, const struct ipset_type *type)
|
||||
{
|
||||
const char * const * alias = type->alias;
|
||||
|
||||
if (STREQ(name, type->name))
|
||||
return true;
|
||||
|
||||
while (alias[0]) {
|
||||
if (STREQ(name, alias[0]))
|
||||
return true;
|
||||
alias++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline const struct ipset_type *
|
||||
create_type_get(struct ipset_session *session)
|
||||
{
|
||||
struct ipset_type *t, *match = NULL;
|
||||
struct ipset_data *data;
|
||||
const char *typename;
|
||||
uint8_t family, tmin = 0, tmax = 0;
|
||||
const uint8_t *kmin, *kmax;
|
||||
int ret;
|
||||
|
||||
data = ipset_session_data(session);
|
||||
assert(data);
|
||||
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
|
||||
assert(typename);
|
||||
family = ipset_data_family(data);
|
||||
|
||||
/* Check registered types in userspace */
|
||||
for (t = typelist; t != NULL; t = t->next) {
|
||||
/* Skip revisions which are unsupported by the kernel */
|
||||
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
|
||||
continue;
|
||||
if (ipset_match_typename(typename, t)
|
||||
&& MATCH_FAMILY(t, family)) {
|
||||
if (match == NULL) {
|
||||
match = t;
|
||||
tmax = t->revision;
|
||||
} else if (t->family == match->family)
|
||||
tmin = t->revision;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
return ipset_errptr(session,
|
||||
"Syntax error: unknown settype %s",
|
||||
typename);
|
||||
|
||||
/* Family is unspecified yet: set from matching set type */
|
||||
if (family == AF_UNSPEC && match->family != AF_UNSPEC) {
|
||||
family = match->family == AF_INET46 ? AF_INET : match->family;
|
||||
ipset_data_set(data, IPSET_OPT_FAMILY, &family);
|
||||
}
|
||||
|
||||
if (match->kernel_check == IPSET_KERNEL_OK)
|
||||
goto found;
|
||||
|
||||
/* Check kernel */
|
||||
ret = ipset_cmd(session, IPSET_CMD_TYPE, 0);
|
||||
if (ret != 0)
|
||||
return NULL;
|
||||
|
||||
kmax = ipset_data_get(data, IPSET_OPT_REVISION);
|
||||
if (ipset_data_test(data, IPSET_OPT_REVISION_MIN))
|
||||
kmin = ipset_data_get(data, IPSET_OPT_REVISION_MIN);
|
||||
else
|
||||
kmin = kmax;
|
||||
if (MAX(tmin, *kmin) > MIN(tmax, *kmax)) {
|
||||
if (*kmin > tmax)
|
||||
return ipset_errptr(session,
|
||||
"Kernel supports %s type with family %s "
|
||||
"in minimal revision %u while ipset library "
|
||||
"in maximal revision %u. "
|
||||
"You need to upgrade your ipset library.",
|
||||
typename,
|
||||
family == AF_INET ? "INET" :
|
||||
family == AF_INET6 ? "INET6" : "UNSPEC",
|
||||
*kmin, tmax);
|
||||
else
|
||||
return ipset_errptr(session,
|
||||
"Kernel supports %s type with family %s "
|
||||
"in maximal revision %u while ipset library "
|
||||
"in minimal revision %u. "
|
||||
"You need to upgrade your kernel.",
|
||||
typename,
|
||||
family == AF_INET ? "INET" :
|
||||
family == AF_INET6 ? "INET6" : "UNSPEC",
|
||||
*kmax, tmin);
|
||||
}
|
||||
|
||||
match->kernel_check = IPSET_KERNEL_OK;
|
||||
found:
|
||||
ipset_data_set(data, IPSET_OPT_TYPE, match);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
#define set_family_and_type(data, match, family) do { \
|
||||
if (family == AF_UNSPEC && match->family != AF_UNSPEC) \
|
||||
family = match->family == AF_INET46 ? AF_INET : match->family;\
|
||||
ipset_data_set(data, IPSET_OPT_FAMILY, &family); \
|
||||
ipset_data_set(data, IPSET_OPT_TYPE, match); \
|
||||
} while (0)
|
||||
|
||||
|
||||
static inline const struct ipset_type *
|
||||
adt_type_get(struct ipset_session *session)
|
||||
{
|
||||
struct ipset_data *data;
|
||||
struct ipset *s;
|
||||
struct ipset_type *t;
|
||||
const struct ipset_type *match;
|
||||
const char *setname, *typename;
|
||||
const uint8_t *revision;
|
||||
uint8_t family = AF_UNSPEC;
|
||||
int ret;
|
||||
|
||||
data = ipset_session_data(session);
|
||||
assert(data);
|
||||
setname = ipset_data_setname(data);
|
||||
assert(setname);
|
||||
|
||||
/* Check existing sets in cache */
|
||||
for (s = setlist; s != NULL; s = s->next) {
|
||||
if (STREQ(setname, s->name)) {
|
||||
ipset_data_set(data, IPSET_OPT_FAMILY, &s->family);
|
||||
ipset_data_set(data, IPSET_OPT_TYPE, s->type);
|
||||
return s->type;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check kernel */
|
||||
ret = ipset_cmd(session, IPSET_CMD_HEADER, 0);
|
||||
if (ret != 0)
|
||||
return NULL;
|
||||
|
||||
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
|
||||
revision = ipset_data_get(data, IPSET_OPT_REVISION);
|
||||
family = ipset_data_family(data);
|
||||
|
||||
/* Check registered types */
|
||||
for (t = typelist, match = NULL;
|
||||
t != NULL && match == NULL; t = t->next) {
|
||||
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
|
||||
continue;
|
||||
if (STREQ(typename, t->name)
|
||||
&& MATCH_FAMILY(t, family)
|
||||
&& *revision == t->revision) {
|
||||
t->kernel_check = IPSET_KERNEL_OK;
|
||||
match = t;
|
||||
}
|
||||
}
|
||||
if (!match)
|
||||
return ipset_errptr(session,
|
||||
"Kernel-library incompatibility: "
|
||||
"set %s in kernel has got settype %s "
|
||||
"with family %s and revision %u while "
|
||||
"ipset library does not support the "
|
||||
"settype with that family and revision.",
|
||||
setname, typename,
|
||||
family == AF_INET ? "inet" :
|
||||
family == AF_INET6 ? "inet6" : "unspec",
|
||||
*revision);
|
||||
|
||||
set_family_and_type(data, match, family);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_type_get - get a set type from the kernel
|
||||
* @session: session structure
|
||||
* @cmd: the command which needs the set type
|
||||
*
|
||||
* Build up and send a private message to the kernel in order to
|
||||
* get the set type. When creating the set, we send the typename
|
||||
* and family and get the supported revisions of the given set type.
|
||||
* When adding/deleting/testing an entry, we send the setname and
|
||||
* receive the typename, family and revision.
|
||||
*
|
||||
* Returns the set type for success and NULL for failure.
|
||||
*/
|
||||
const struct ipset_type *
|
||||
ipset_type_get(struct ipset_session *session, enum ipset_cmd cmd)
|
||||
{
|
||||
assert(session);
|
||||
|
||||
switch (cmd) {
|
||||
case IPSET_CMD_CREATE:
|
||||
return create_type_get(session);
|
||||
case IPSET_CMD_ADD:
|
||||
case IPSET_CMD_DEL:
|
||||
case IPSET_CMD_TEST:
|
||||
return adt_type_get(session);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
assert(cmd == IPSET_CMD_NONE);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_type_check - check the set type received from kernel
|
||||
* @session: session structure
|
||||
*
|
||||
* Check the set type received from the kernel (typename, revision,
|
||||
* family) against the userspace types looking for a matching type.
|
||||
*
|
||||
* Returns the set type for success and NULL for failure.
|
||||
*/
|
||||
const struct ipset_type *
|
||||
ipset_type_check(struct ipset_session *session)
|
||||
{
|
||||
const struct ipset_type *t, *match = NULL;
|
||||
struct ipset_data *data;
|
||||
const char *typename;
|
||||
uint8_t family = AF_UNSPEC, revision;
|
||||
|
||||
assert(session);
|
||||
data = ipset_session_data(session);
|
||||
assert(data);
|
||||
|
||||
typename = ipset_data_get(data, IPSET_OPT_TYPENAME);
|
||||
family = ipset_data_family(data);
|
||||
revision = *(const uint8_t *) ipset_data_get(data, IPSET_OPT_REVISION);
|
||||
|
||||
/* Check registered types */
|
||||
for (t = typelist; t != NULL && match == NULL; t = t->next) {
|
||||
if (t->kernel_check == IPSET_KERNEL_MISMATCH)
|
||||
continue;
|
||||
if (ipset_match_typename(typename, t)
|
||||
&& MATCH_FAMILY(t, family)
|
||||
&& t->revision == revision)
|
||||
match = t;
|
||||
}
|
||||
if (!match)
|
||||
return ipset_errptr(session,
|
||||
"Kernel and userspace incompatible: "
|
||||
"settype %s with revision %u not supported ",
|
||||
"by userspace.", typename, revision);
|
||||
|
||||
set_family_and_type(data, match, family);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_type_add - add (register) a userspace set type
|
||||
* @type: pointer to the set type structure
|
||||
*
|
||||
* Add the given set type to the type list. The types
|
||||
* are added sorted, in descending revision number.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_type_add(struct ipset_type *type)
|
||||
{
|
||||
struct ipset_type *t, *prev;
|
||||
|
||||
assert(type);
|
||||
|
||||
/* Add to the list: higher revision numbers first */
|
||||
for (t = typelist, prev = NULL; t != NULL; t = t->next) {
|
||||
if (STREQ(t->name, type->name)) {
|
||||
if (t->revision == type->revision) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
} else if (t->revision < type->revision) {
|
||||
type->next = t;
|
||||
if (prev)
|
||||
prev->next = type;
|
||||
else
|
||||
typelist = type;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (t->next != NULL && STREQ(t->next->name, type->name)) {
|
||||
if (t->next->revision == type->revision) {
|
||||
errno = EEXIST;
|
||||
return -1;
|
||||
} else if (t->next->revision < type->revision) {
|
||||
type->next = t->next;
|
||||
t->next = type;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
prev = t;
|
||||
}
|
||||
type->next = typelist;
|
||||
typelist = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_typename_resolve - resolve typename alias
|
||||
* @str: typename or alias
|
||||
*
|
||||
* Check the typenames (and aliases) and return the
|
||||
* preferred name of the set type.
|
||||
*
|
||||
* Returns the name of the matching set type or NULL.
|
||||
*/
|
||||
const char *
|
||||
ipset_typename_resolve(const char *str)
|
||||
{
|
||||
const struct ipset_type *t;
|
||||
|
||||
for (t = typelist; t != NULL; t = t->next)
|
||||
if (ipset_match_typename(str, t))
|
||||
return t->name;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_types - return the list of the set types
|
||||
*
|
||||
* The types can be unchecked with respect of the running kernel.
|
||||
* Only useful for type specific help.
|
||||
*
|
||||
* Returns the list of the set types.
|
||||
*/
|
||||
const struct ipset_type *
|
||||
ipset_types(void)
|
||||
{
|
||||
return typelist;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_cache_init - initialize set cache
|
||||
*
|
||||
* Initialize the set cache in userspace.
|
||||
*
|
||||
* Returns 0 on success or a negative error code.
|
||||
*/
|
||||
int
|
||||
ipset_cache_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipset_cache_fini - release the set cache
|
||||
*
|
||||
* Release the set cache.
|
||||
*/
|
||||
void
|
||||
ipset_cache_fini(void)
|
||||
{
|
||||
struct ipset *set;
|
||||
|
||||
while (setlist) {
|
||||
set = setlist;
|
||||
setlist = setlist->next;
|
||||
free(set);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user