Skip to content

Commit

Permalink
Progress on #390
Browse files Browse the repository at this point in the history
Simple implementation; still a prototype. Seems to work fine.
Most crippling issue is that blocks don't deallocate on their own yet.

Usage:

	jool instance add ...
	jool p4block add 192.0.2.1:1024-1535
	jool p4block add 192.0.2.1:1536-2047
	jool p4block add 192.0.2.1:2048-2559
	...
	jool p4block display
	dmesg | tail

This replaces the old pool4.

Known TODOs:

- Blocks need to be added one by one. Would be nice if they could be
  algorithmically generated.
- `p4block display` currently prints the table in dmesg, not standard
  output.
- `p4block add` doesn't validate collision. This validation is currently
  deferred to the user.
  (Although I'm not really sure if this needs to be fixed. The end
  result is that some clients will share transport addresses, which
  doesn't really matter because they will still compete for them.)
- There's no switch to change back to the old pool4.
- Block allocation and deallocation logs are still absent.
- There's no deallocation timer yet.
- Atomic configuration doesn't work.
- TCP, UDP and ICMP currently can't have different blocks. (Meh)
- 4->6 side can probably be optimized more.
- Randomize ports? Meeeeeeeeeeh
  • Loading branch information
ydahhrk committed Nov 28, 2022
1 parent 226b37e commit 0c51982
Show file tree
Hide file tree
Showing 30 changed files with 974 additions and 16 deletions.
6 changes: 6 additions & 0 deletions src/common/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ struct nla_policy joolnl_pool4_entry_policy[JNLAP4_COUNT] = {
[JNLAP4_PORT_MAX] = { .type = NLA_U16 },
};

struct nla_policy joolnl_p4block_policy[JNLAPB_COUNT] = {
[JNLAPB_ADDR] = JOOLNL_ADDR4_POLICY,
[JNLAPB_PORT_MIN] = { .type = NLA_U16 },
[JNLAPB_PORT_MAX] = { .type = NLA_U16 },
};

struct nla_policy joolnl_bib_entry_policy[JNLAB_COUNT] = {
[JNLAB_SRC6] = { .type = NLA_NESTED },
[JNLAB_SRC4] = { .type = NLA_NESTED },
Expand Down
14 changes: 14 additions & 0 deletions src/common/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ enum joolnl_operation {
JNLOP_POOL4_RM,
JNLOP_POOL4_FLUSH,

JNLOP_P4BLOCK_FOREACH,
JNLOP_P4BLOCK_ADD,
JNLOP_P4BLOCK_RM,

JNLOP_BIB_FOREACH,
JNLOP_BIB_ADD,
JNLOP_BIB_RM,
Expand Down Expand Up @@ -178,6 +182,16 @@ enum joolnl_attr_pool4 {

extern struct nla_policy joolnl_pool4_entry_policy[JNLAP4_COUNT];

enum joolnl_attr_p4block {
JNLAPB_ADDR = 1,
JNLAPB_PORT_MIN,
JNLAPB_PORT_MAX,
JNLAPB_COUNT,
#define JNLAPB_MAX (JNLAPB_COUNT - 1)
};

extern struct nla_policy joolnl_p4block_policy[JNLAPB_COUNT];

enum joolnl_attr_bib {
JNLAB_SRC6 = 1,
JNLAB_SRC4,
Expand Down
5 changes: 5 additions & 0 deletions src/common/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ struct pool4_entry {
struct ipv4_range range;
};

struct p4block {
struct in_addr addr;
struct port_range ports;
};

/*
* A mask that dictates which IPv4 transport address is being used to mask a
* given IPv6 (transport) client.
Expand Down
4 changes: 2 additions & 2 deletions src/mod/common/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ ccflags-y := -I$(src)/../..

obj-m += jool_common.o


jool_common-objs += rfc7915/4to6.o
jool_common-objs += rfc7915/6to4.o
jool_common-objs += rfc7915/common.o
Expand Down Expand Up @@ -37,7 +36,6 @@ jool_common-objs += wrapper-config.o
jool_common-objs += wrapper-global.o
jool_common-objs += wrapper-types.o
jool_common-objs += xlator.o

jool_common-objs += steps/send_packet.o

jool_common-objs += nl/address.o
Expand All @@ -52,6 +50,7 @@ jool_common-objs += nl/global.o
jool_common-objs += nl/instance.o
jool_common-objs += nl/joold.o
jool_common-objs += nl/nl_common.o
jool_common-objs += nl/p4block.o
jool_common-objs += nl/pool4.o
jool_common-objs += nl/session.o
jool_common-objs += nl/stats.o
Expand All @@ -66,6 +65,7 @@ jool_common-objs += db/rfc6791v6.o
jool_common-objs += db/pool4/empty.o
jool_common-objs += db/pool4/db.o
jool_common-objs += db/pool4/rfc6056.o
jool_common-objs += db/pool4-v2/block.o

jool_common-objs += db/bib/db.o
jool_common-objs += db/bib/entry.o
Expand Down
286 changes: 286 additions & 0 deletions src/mod/common/db/pool4-v2/block.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
#include "mod/common/db/pool4-v2/block.h"

#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/hashtable.h>

#include "mod/common/address.h"
#include "mod/common/log.h"

struct block {
struct p4block blk;

struct in6_addr assigned_addr6; /* Currently assigned client */
u64 last_used_time;

bool is_assigned;
union {
/* is_assigned false */
struct list_head list; /* For @idle_blocks */
/* is_assigned true */
struct hlist_node htable; /* For @assignments */
} hook;
};

#define HTABLE_BITS 8

struct p4blocks {
/* Length of @blocks and @assignments */
size_t total_blocks;
/* Extract from @blocks which haven't yet assigned to any IPv6 addresses */
struct list_head idle_blocks;
/*
* Extract from @blocks which have been assigned IPv6 addresses, indexed by IPv6
* address.
*/
DECLARE_HASHTABLE(assignments, HTABLE_BITS);

spinlock_t lock;
struct kref refs;
};

struct p4blocks *p4block_init(void)
{
struct p4blocks *result;

result = kmalloc(sizeof(struct p4blocks), GFP_KERNEL);
if (!result)
return NULL;

result->total_blocks = 0;
INIT_LIST_HEAD(&result->idle_blocks);
hash_init(result->assignments);
spin_lock_init(&result->lock);
kref_init(&result->refs);

return result;
}

void p4block_get(struct p4blocks *blocks)
{
kref_get(&blocks->refs);
}

static void p4block_release(struct kref *refs)
{
struct p4blocks *blocks;
struct block *blk;
struct hlist_node *tmp;
size_t b;

blocks = container_of(refs, struct p4blocks, refs);

while (!list_empty(&blocks->idle_blocks)) {
blk = list_first_entry(&blocks->idle_blocks, struct block,
hook.list);
list_del(&blk->hook.list);
kfree(blk);
}

hash_for_each_safe(blocks->assignments, b, tmp, blk, hook.htable) {
hash_del(&blk->hook.htable);
kfree(blk);
}

kfree(blocks);
}

void p4block_put(struct p4blocks *blocks)
{
kref_put(&blocks->refs, p4block_release);
}

int p4block_add(struct p4blocks *blocks, struct p4block *addend)
{
struct block *node;

node = kmalloc(sizeof(struct block), GFP_KERNEL);
if (!node)
return -ENOMEM;
node->blk = *addend;
node->is_assigned = false;

spin_lock_bh(&blocks->lock);
list_add_tail(&node->hook.list, &blocks->idle_blocks);
spin_unlock_bh(&blocks->lock);

return 0;
}

static bool block_equals(struct block *a, struct p4block *b)
{
return addr4_equals(&a->blk.addr, &b->addr)
&& (a->blk.ports.min == b->ports.min)
&& (a->blk.ports.max == b->ports.max);
}

int p4block_rm(struct p4blocks *blocks, struct p4block *subtrahend)
{
size_t b;
struct block *node;

spin_lock_bh(&blocks->lock);

list_for_each_entry(node, &blocks->idle_blocks, hook.list) {
if (block_equals(node, subtrahend)) {
list_del(&node->hook.list);
goto success;
}
}

hash_for_each(blocks->assignments, b, node, hook.htable) {
if (block_equals(node, subtrahend)) {
hash_del(&node->hook.htable);
goto success;
}
}

spin_unlock_bh(&blocks->lock);
return -ESRCH;

success:
spin_unlock_bh(&blocks->lock);
kfree(node);
return 0;
}

void p4block_print(struct p4blocks *blocks, const char *prefix)
{
size_t b;
struct block *node;

pr_info("%s: {\n", prefix ? prefix : "Blocks");
list_for_each_entry(node, &blocks->idle_blocks, hook.list) {
pr_info(" %pI4c:%u-%u\n", &node->blk.addr, node->blk.ports.min,
node->blk.ports.max);
}
hash_for_each(blocks->assignments, b, node, hook.htable) {
pr_info(" %pI4c:%u-%u (assigned to %pI6c)\n", &node->blk.addr,
node->blk.ports.min, node->blk.ports.max,
&node->assigned_addr6);
}
pr_info("}\n");
}

static u16 hash_addr6(const struct in6_addr *addr6)
{
__u32 q3;
__u32 q7;

q3 = be16_to_cpu(addr6->s6_addr16[3]);
q7 = be16_to_cpu(addr6->s6_addr16[7]);

return hash_32((q3 << 16) | q7, HTABLE_BITS);
}

static struct block *get_next_unused_block(struct p4blocks *blocks)
{
if (list_empty(&blocks->idle_blocks))
return NULL;
return list_first_entry(&blocks->idle_blocks, struct block, hook.list);
}

int p4block_find(struct p4blocks *blocks, struct in6_addr *client,
struct p4block *result)
{
struct block *db_node;
u64 now;
__u16 hash;

now = get_jiffies_64();
hash = hash_addr6(client);

spin_lock_bh(&blocks->lock);

/* If already assigned, return assigned block */
hlist_for_each_entry(db_node, &blocks->assignments[hash], hook.htable)
if (addr6_equals(&db_node->assigned_addr6, client))
goto success;

db_node = get_next_unused_block(blocks);
if (db_node == NULL) {
spin_unlock_bh(&blocks->lock);
log_warn_once(
"Client %pI6c needs a pool4 block, but I already ran out.",
client
);
return -ESRCH;
}

list_del(&db_node->hook.list);
hlist_add_head(&db_node->hook.htable, &blocks->assignments[hash]);
db_node->assigned_addr6 = *client;
db_node->is_assigned = true;
/* Fall through */

success:
db_node->last_used_time = now;
*result = db_node->blk;
spin_unlock_bh(&blocks->lock);
return 0;
}

bool addr4_matches_blk(struct p4block const *blk,
struct ipv4_transport_addr const *addr4)
{
return addr4_equals(&blk->addr, &addr4->l3)
&& (blk->ports.min <= addr4->l4)
&& (addr4->l4 <= blk->ports.max);
}

bool p4block_contains(struct p4blocks *blocks,
struct ipv4_transport_addr const *addr)
{
size_t b;
struct block *node;

spin_lock_bh(&blocks->lock);

list_for_each_entry(node, &blocks->idle_blocks, hook.list)
if (addr4_matches_blk(&node->blk, addr))
goto yes;
hash_for_each(blocks->assignments, b, node, hook.htable)
if (addr4_matches_blk(&node->blk, addr))
goto yes;

spin_unlock_bh(&blocks->lock);
return false;

yes:
spin_unlock_bh(&blocks->lock);
return true;
}

void p4block_expire(struct p4blocks *blocks, u64 time_limit)
{
u64 now;
struct block *blk;
struct hlist_node *tmp;
size_t b;

now = get_jiffies_64();

spin_lock_bh(&blocks->lock);
hash_for_each_safe(blocks->assignments, b, tmp, blk, hook.htable) {
if (now - blk->last_used_time > time_limit) {
blk->is_assigned = false;
hash_del(&blk->hook.htable);
list_add_tail(&blk->hook.list, &blocks->idle_blocks);
}
}
spin_unlock_bh(&blocks->lock);
}

/* TODO fix unit tests and remove this */
void p4block_cheat(struct p4blocks *blocks)
{
size_t b;
struct block *node;

hash_for_each(blocks->assignments, b, node, hook.htable) {
node->last_used_time -= 100000;
}
}
26 changes: 26 additions & 0 deletions src/mod/common/db/pool4-v2/block.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifndef SRC_MOD_COMMON_DB_POOL4_V2_BLOCK_H_
#define SRC_MOD_COMMON_DB_POOL4_V2_BLOCK_H_

#include "common/types.h"

struct p4blocks;

/* Constructors, destructors */
struct p4blocks *p4block_init(void);
void p4block_get(struct p4blocks *);
void p4block_put(struct p4blocks *);

/* Userspace client */
int p4block_add(struct p4blocks *, struct p4block *);
int p4block_rm(struct p4blocks *, struct p4block *);
void p4block_print(struct p4blocks *, const char *);

/* Translation */
int p4block_find(struct p4blocks *, struct in6_addr *, struct p4block *);
bool p4block_contains(struct p4blocks *, struct ipv4_transport_addr const *);
void p4block_expire(struct p4blocks *, u64 time_limit);

/* Testing */
void p4block_cheat(struct p4blocks *blocks);

#endif /* SRC_MOD_COMMON_DB_POOL4_V2_BLOCK_H_ */
Loading

0 comments on commit 0c51982

Please sign in to comment.