summaryrefslogtreecommitdiffstats
path: root/src/netlink.c
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2020-04-24 21:56:46 +0200
committerPablo Neira Ayuso <pablo@netfilter.org>2020-04-28 17:32:31 +0200
commit9599d9d25a6b383b72b119c709af33f6f6031786 (patch)
tree2ac2f19b9f5f55578ab6cebe25ebb35dea659492 /src/netlink.c
parent2885cf2e65042b3dbc44fc232fd35840df255935 (diff)
src: NAT support for intervals in maps
This patch allows you to specify an interval of IP address in maps. table ip x { chain y { type nat hook postrouting priority srcnat; policy accept; snat ip interval to ip saddr map { 10.141.11.4 : 192.168.2.2-192.168.2.4 } } } The example above performs SNAT to packets that comes from 10.141.11.4 to an interval of IP addresses from 192.168.2.2 to 192.168.2.4 (both included). You can also combine this with dynamic maps: table ip x { map y { type ipv4_addr : interval ipv4_addr flags interval elements = { 10.141.10.0/24 : 192.168.2.2-192.168.2.4 } } chain y { type nat hook postrouting priority srcnat; policy accept; snat ip interval to ip saddr map @y } } Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Diffstat (limited to 'src/netlink.c')
-rw-r--r--src/netlink.c111
1 files changed, 110 insertions, 1 deletions
diff --git a/src/netlink.c b/src/netlink.c
index 7b7ef39e..10964720 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -176,6 +176,8 @@ static struct nftnl_set_elem *alloc_nftnl_setelem(const struct expr *set,
assert(nld.len > 0);
/* fallthrough */
case EXPR_VALUE:
+ case EXPR_RANGE:
+ case EXPR_PREFIX:
nftnl_set_elem_set(nlse, NFTNL_SET_ELEM_DATA,
nld.value, nld.len);
break;
@@ -296,6 +298,38 @@ static void netlink_gen_verdict(const struct expr *expr,
}
}
+static void netlink_gen_range(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = div_round_up(expr->left->len, BITS_PER_BYTE) * 2;
+ unsigned char data[len];
+ unsigned int offset = 0;
+
+ memset(data, 0, len);
+ offset = netlink_export_pad(data, expr->left->value, expr->left);
+ netlink_export_pad(data + offset, expr->right->value, expr->right);
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
+static void netlink_gen_prefix(const struct expr *expr,
+ struct nft_data_linearize *nld)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE) * 2;
+ unsigned char data[len];
+ int offset;
+ mpz_t v;
+
+ offset = netlink_export_pad(data, expr->prefix->value, expr);
+ mpz_init_bitmask(v, expr->len - expr->prefix_len);
+ mpz_add(v, expr->prefix->value, v);
+ netlink_export_pad(data + offset, v, expr->prefix);
+ mpz_clear(v);
+
+ memcpy(nld->value, data, len);
+ nld->len = len;
+}
+
void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
{
switch (expr->etype) {
@@ -305,6 +339,10 @@ void netlink_gen_data(const struct expr *expr, struct nft_data_linearize *data)
return netlink_gen_concat_data(expr, data);
case EXPR_VERDICT:
return netlink_gen_verdict(expr, data);
+ case EXPR_RANGE:
+ return netlink_gen_range(expr, data);
+ case EXPR_PREFIX:
+ return netlink_gen_prefix(expr, data);
default:
BUG("invalid data expression type %s\n", expr_name(expr));
}
@@ -618,6 +656,7 @@ static int set_parse_udata_cb(const struct nftnl_udata *attr, void *data)
case NFTNL_UDATA_SET_KEYBYTEORDER:
case NFTNL_UDATA_SET_DATABYTEORDER:
case NFTNL_UDATA_SET_MERGE_ELEMENTS:
+ case NFTNL_UDATA_SET_DATA_INTERVAL:
if (len != sizeof(uint32_t))
return -1;
break;
@@ -701,6 +740,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
struct expr *typeof_expr_key, *typeof_expr_data;
uint32_t flags, key, objtype = 0;
const struct datatype *dtype;
+ uint32_t data_interval = 0;
bool automerge = false;
const char *udata;
struct set *set;
@@ -724,6 +764,7 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
GET_U32_UDATA(keybyteorder, NFTNL_UDATA_SET_KEYBYTEORDER);
GET_U32_UDATA(databyteorder, NFTNL_UDATA_SET_DATABYTEORDER);
GET_U32_UDATA(automerge, NFTNL_UDATA_SET_MERGE_ELEMENTS);
+ GET_U32_UDATA(data_interval, NFTNL_UDATA_SET_DATA_INTERVAL);
#undef GET_U32_UDATA
typeof_expr_key = set_make_key(ud[NFTNL_UDATA_SET_KEY_TYPEOF]);
@@ -792,6 +833,9 @@ struct set *netlink_delinearize_set(struct netlink_ctx *ctx,
typeof_expr_key = NULL;
}
+ if (data_interval)
+ set->data->flags |= EXPR_F_INTERVAL;
+
if (dtype != datatype)
datatype_free(datatype);
}
@@ -885,6 +929,69 @@ void alloc_setelem_cache(const struct expr *set, struct nftnl_set *nls)
}
}
+static bool mpz_bitmask_is_prefix(mpz_t bitmask, uint32_t len)
+{
+ unsigned long n1, n2;
+
+ n1 = mpz_scan0(bitmask, 0);
+ if (n1 == ULONG_MAX)
+ return false;
+
+ n2 = mpz_scan1(bitmask, n1 + 1);
+ if (n2 < len)
+ return false;
+
+ return true;
+}
+
+static uint32_t mpz_bitmask_to_prefix(mpz_t bitmask, uint32_t len)
+{
+ return len - mpz_scan0(bitmask, 0);
+}
+
+static struct expr *expr_range_to_prefix(struct expr *range)
+{
+ struct expr *left = range->left, *right = range->right, *prefix;
+ uint32_t len = left->len, prefix_len;
+ mpz_t bitmask;
+
+ mpz_init2(bitmask, len);
+ mpz_xor(bitmask, left->value, right->value);
+
+ if (mpz_bitmask_is_prefix(bitmask, len)) {
+ prefix_len = mpz_bitmask_to_prefix(bitmask, len);
+ prefix = prefix_expr_alloc(&range->location, expr_get(left),
+ prefix_len);
+ mpz_clear(bitmask);
+ expr_free(range);
+
+ return prefix;
+ }
+ mpz_clear(bitmask);
+
+ return range;
+}
+
+static struct expr *netlink_parse_interval_elem(const struct datatype *dtype,
+ struct expr *expr)
+{
+ unsigned int len = div_round_up(expr->len, BITS_PER_BYTE);
+ struct expr *range, *left, *right;
+ char data[len];
+
+ mpz_export_data(data, expr->value, dtype->byteorder, len);
+ left = constant_expr_alloc(&internal_location, dtype,
+ dtype->byteorder,
+ (len / 2) * BITS_PER_BYTE, &data[0]);
+ right = constant_expr_alloc(&internal_location, dtype,
+ dtype->byteorder,
+ (len / 2) * BITS_PER_BYTE, &data[len / 2]);
+ range = range_expr_alloc(&expr->location, left, right);
+ expr_free(expr);
+
+ return expr_range_to_prefix(range);
+}
+
static struct expr *netlink_parse_concat_elem(const struct datatype *dtype,
struct expr *data)
{
@@ -1021,7 +1128,9 @@ key_end:
datatype_set(data, set->data->dtype);
data->byteorder = set->data->byteorder;
- if (set->data->dtype->subtypes)
+ if (set->data->flags & EXPR_F_INTERVAL)
+ data = netlink_parse_interval_elem(set->data->dtype, data);
+ else if (set->data->dtype->subtypes)
data = netlink_parse_concat_elem(set->data->dtype, data);
if (data->byteorder == BYTEORDER_HOST_ENDIAN)