summaryrefslogtreecommitdiff
path: root/src/ipcpd/unicast/pff
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-07-29 18:47:41 +0200
committerSander Vrijders <sander@ouroboros.rocks>2026-08-03 08:49:59 +0200
commitadd9f4a99cc358749b276157d9957c527fef4fe7 (patch)
tree25b968b54abd7d4f8a568c8d4dc62442356e70a6 /src/ipcpd/unicast/pff
parent3388bd9e9b82df6d7fe79fa4eee7c22a3277624e (diff)
downloadouroboros-add9f4a99cc358749b276157d9957c527fef4fe7.tar.gz
ouroboros-add9f4a99cc358749b276157d9957c527fef4fe7.zip
ipcpd: Flush fd from pff after disconnect
The flow descriptors were not immediately removed from the routing tables when they were disconnected, so forwarding paths could still use the fd after the IPCP has deallocated the flow. Refactors the tests and adds some for the del_fd function. Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/ipcpd/unicast/pff')
-rw-r--r--src/ipcpd/unicast/pff/multipath.c20
-rw-r--r--src/ipcpd/unicast/pff/multipath.h4
-rw-r--r--src/ipcpd/unicast/pff/pft.c37
-rw-r--r--src/ipcpd/unicast/pff/pft.h3
-rw-r--r--src/ipcpd/unicast/pff/simple.c20
-rw-r--r--src/ipcpd/unicast/pff/simple.h4
-rw-r--r--src/ipcpd/unicast/pff/tests/pft_test.c300
7 files changed, 348 insertions, 40 deletions
diff --git a/src/ipcpd/unicast/pff/multipath.c b/src/ipcpd/unicast/pff/multipath.c
index 9ba59592..c2c21078 100644
--- a/src/ipcpd/unicast/pff/multipath.c
+++ b/src/ipcpd/unicast/pff/multipath.c
@@ -49,7 +49,7 @@ struct pff_ops multipath_pff_ops = {
.del = multipath_pff_del,
.flush = multipath_pff_flush,
.nhop = multipath_pff_nhop,
- .flow_state_change = NULL
+ .flow_state_change = multipath_pff_flow_state_change
};
struct pff_i * multipath_pff_create(void)
@@ -170,6 +170,24 @@ void multipath_pff_flush(struct pff_i * pff_i)
pft_flush(pff_i->pft);
}
+int multipath_pff_flow_state_change(struct pff_i * pff_i,
+ int fd,
+ bool up)
+{
+ assert(pff_i);
+
+ if (up)
+ return 0;
+
+ pthread_rwlock_wrlock(&pff_i->lock);
+
+ pft_del_fd(pff_i->pft, fd);
+
+ pthread_rwlock_unlock(&pff_i->lock);
+
+ return 0;
+}
+
int multipath_pff_nhop(struct pff_i * pff_i,
uint64_t addr)
{
diff --git a/src/ipcpd/unicast/pff/multipath.h b/src/ipcpd/unicast/pff/multipath.h
index 5329f7fc..123030b6 100644
--- a/src/ipcpd/unicast/pff/multipath.h
+++ b/src/ipcpd/unicast/pff/multipath.h
@@ -53,6 +53,10 @@ void multipath_pff_flush(struct pff_i * pff_i);
int multipath_pff_nhop(struct pff_i * pff_i,
uint64_t addr);
+int multipath_pff_flow_state_change(struct pff_i * pff_i,
+ int fd,
+ bool up);
+
extern struct pff_ops multipath_pff_ops;
#endif /* OUROBOROS_IPCPD_UNICAST_MULTIPATH_PFF_H */
diff --git a/src/ipcpd/unicast/pff/pft.c b/src/ipcpd/unicast/pff/pft.c
index d0e562d6..2a295a40 100644
--- a/src/ipcpd/unicast/pff/pft.c
+++ b/src/ipcpd/unicast/pff/pft.c
@@ -91,6 +91,43 @@ void pft_destroy(struct pft * pft)
free(pft);
}
+void pft_del_fd(struct pft * pft,
+ int fd)
+{
+ unsigned int i;
+ struct list_head * p;
+ struct list_head * h;
+ struct pft_entry * entry;
+ size_t j;
+ size_t n;
+
+ assert(pft);
+
+ for (i = 0; i < pft->buckets_size; i++) {
+ list_for_each_safe(p, h, &(pft->buckets[i])) {
+ entry = list_entry(p, struct pft_entry, next);
+
+ n = 0;
+ for (j = 0; j < entry->len; j++) {
+ if (entry->fds[j] != fd)
+ entry->fds[n++] = entry->fds[j];
+ }
+
+ if (n == entry->len)
+ continue;
+
+ if (n > 0) {
+ entry->len = n;
+ continue;
+ }
+
+ list_del(&entry->next);
+ free(entry->fds);
+ free(entry);
+ }
+ }
+}
+
void pft_flush(struct pft * pft)
{
unsigned int i;
diff --git a/src/ipcpd/unicast/pff/pft.h b/src/ipcpd/unicast/pff/pft.h
index 15bbe451..3517e0ef 100644
--- a/src/ipcpd/unicast/pff/pft.h
+++ b/src/ipcpd/unicast/pff/pft.h
@@ -35,6 +35,9 @@ void pft_destroy(struct pft * table);
void pft_flush(struct pft * table);
+void pft_del_fd(struct pft * table,
+ int fd);
+
/* Passes ownership of the block of memory */
int pft_insert(struct pft * pft,
uint64_t dst,
diff --git a/src/ipcpd/unicast/pff/simple.c b/src/ipcpd/unicast/pff/simple.c
index 7befa42f..4347dcba 100644
--- a/src/ipcpd/unicast/pff/simple.c
+++ b/src/ipcpd/unicast/pff/simple.c
@@ -47,7 +47,7 @@ struct pff_ops simple_pff_ops = {
.del = simple_pff_del,
.flush = simple_pff_flush,
.nhop = simple_pff_nhop,
- .flow_state_change = NULL
+ .flow_state_change = simple_pff_flow_state_change
};
struct pff_i * simple_pff_create(void)
@@ -170,6 +170,24 @@ void simple_pff_flush(struct pff_i * pff_i)
pft_flush(pff_i->pft);
}
+int simple_pff_flow_state_change(struct pff_i * pff_i,
+ int fd,
+ bool up)
+{
+ assert(pff_i);
+
+ if (up)
+ return 0;
+
+ pthread_rwlock_wrlock(&pff_i->lock);
+
+ pft_del_fd(pff_i->pft, fd);
+
+ pthread_rwlock_unlock(&pff_i->lock);
+
+ return 0;
+}
+
int simple_pff_nhop(struct pff_i * pff_i,
uint64_t addr)
{
diff --git a/src/ipcpd/unicast/pff/simple.h b/src/ipcpd/unicast/pff/simple.h
index 1046e4c4..b72aba21 100644
--- a/src/ipcpd/unicast/pff/simple.h
+++ b/src/ipcpd/unicast/pff/simple.h
@@ -52,6 +52,10 @@ void simple_pff_flush(struct pff_i * pff_i);
int simple_pff_nhop(struct pff_i * pff_i,
uint64_t addr);
+int simple_pff_flow_state_change(struct pff_i * pff_i,
+ int fd,
+ bool up);
+
extern struct pff_ops simple_pff_ops;
#endif /* OUROBOROS_IPCPD_UNICAST_SIMPLE_PFF_H */
diff --git a/src/ipcpd/unicast/pff/tests/pft_test.c b/src/ipcpd/unicast/pff/tests/pft_test.c
index 20e73a94..0b4a165b 100644
--- a/src/ipcpd/unicast/pff/tests/pft_test.c
+++ b/src/ipcpd/unicast/pff/tests/pft_test.c
@@ -22,97 +22,321 @@
#include "pft.c"
+#include <test/test.h>
+
#include <stdio.h>
#define TBL_SIZE 256
#define INT_TEST 4
-int pft_test(int argc,
- char ** argv)
+/* Next hops used by the del_fd tests. */
+#define FD_GONE 7
+#define FD_KEEP 8
+#define FD_OTHER 9
+
+static int pft_add(struct pft * pft,
+ uint64_t dst,
+ const int * fds,
+ size_t len)
+{
+ int * blk;
+ size_t i;
+
+ blk = malloc(sizeof(*blk) * len);
+ if (blk == NULL)
+ return -1;
+
+ for (i = 0; i < len; i++)
+ blk[i] = fds[i];
+
+ if (pft_insert(pft, dst, blk, len)) {
+ free(blk);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_pft_create_destroy(void)
+{
+ struct pft * pft;
+
+ TEST_START();
+
+ pft = pft_create(TBL_SIZE);
+ if (pft == NULL) {
+ printf("Failed to create.\n");
+ goto fail;
+ }
+
+ pft_destroy(pft);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_pft_insert_lookup(void)
{
struct pft * pft;
- int i;
int * j;
size_t len;
+ int i;
- (void) argc;
- (void) argv;
+ TEST_START();
pft = pft_create(TBL_SIZE);
if (pft == NULL) {
printf("Failed to create.\n");
- return -1;
+ goto fail;
}
for (i = 0; i < TBL_SIZE + INT_TEST + 2; i++) {
- j = malloc(sizeof(*j));
- if (j == NULL) {
- printf("Failed to malloc.\n");
- pft_destroy(pft);
- return -1;
- }
- *j = i;
-
- if (pft_insert(pft, i, j, 1)) {
+ if (pft_add(pft, i, &i, 1)) {
printf("Failed to insert.\n");
- pft_destroy(pft);
- free(j);
- return -1;
+ goto fail_pft;
}
}
if (pft_lookup(pft, INT_TEST, &j, &len)) {
printf("Failed to lookup.\n");
- pft_destroy(pft);
- return -1;
+ goto fail_pft;
}
if (*j != INT_TEST) {
printf("Lookup returned wrong value (%d != %d).\n",
INT_TEST, *j);
- pft_destroy(pft);
- return -1;
+ goto fail_pft;
}
if (pft_lookup(pft, TBL_SIZE + INT_TEST, &j, &len)) {
- printf("Failed to lookup.\n");
- pft_destroy(pft);
- return -1;
+ printf("Failed to lookup on a shared bucket.\n");
+ goto fail_pft;
}
if (*j != TBL_SIZE + INT_TEST) {
printf("Lookup returned wrong value (%d != %d).\n",
- INT_TEST, *j);
- pft_destroy(pft);
- return -1;
+ TBL_SIZE + INT_TEST, *j);
+ goto fail_pft;
+ }
+
+ pft_destroy(pft);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_pft:
+ pft_destroy(pft);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_pft_delete(void)
+{
+ struct pft * pft;
+ int * j;
+ size_t len;
+ int i;
+
+ TEST_START();
+
+ pft = pft_create(TBL_SIZE);
+ if (pft == NULL) {
+ printf("Failed to create.\n");
+ goto fail;
+ }
+
+ for (i = 0; i < TBL_SIZE + INT_TEST + 2; i++) {
+ if (pft_add(pft, i, &i, 1)) {
+ printf("Failed to insert.\n");
+ goto fail_pft;
+ }
}
if (pft_delete(pft, INT_TEST)) {
printf("Failed to delete.\n");
- pft_destroy(pft);
- return -1;
+ goto fail_pft;
}
if (pft_lookup(pft, INT_TEST, &j, &len) == 0) {
printf("Failed to delete properly.\n");
- pft_destroy(pft);
- return -1;
+ goto fail_pft;
}
if (pft_lookup(pft, TBL_SIZE + INT_TEST, &j, &len)) {
printf("Failed to lookup after deletion.\n");
- pft_destroy(pft);
- return -1;
+ goto fail_pft;
}
if (*j != TBL_SIZE + INT_TEST) {
printf("Lookup returned wrong value (%d != %d).\n",
- INT_TEST, *j);
- pft_destroy(pft);
- return -1;
+ TBL_SIZE + INT_TEST, *j);
+ goto fail_pft;
}
pft_destroy(pft);
- return 0;
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_pft:
+ pft_destroy(pft);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_pft_del_fd_sole(void)
+{
+ struct pft * pft;
+ int * j;
+ size_t len;
+ int fds[] = {FD_GONE};
+
+ TEST_START();
+
+ pft = pft_create(TBL_SIZE);
+ if (pft == NULL) {
+ printf("Failed to create.\n");
+ goto fail;
+ }
+
+ if (pft_add(pft, INT_TEST, fds, 1)) {
+ printf("Failed to insert.\n");
+ goto fail_pft;
+ }
+
+ pft_del_fd(pft, FD_GONE);
+
+ if (pft_lookup(pft, INT_TEST, &j, &len) == 0) {
+ printf("Route without a next hop survived.\n");
+ goto fail_pft;
+ }
+
+ pft_destroy(pft);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_pft:
+ pft_destroy(pft);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_pft_del_fd_shared(void)
+{
+ struct pft * pft;
+ int * j;
+ size_t len;
+ int fds[] = {FD_KEEP, FD_GONE, FD_OTHER};
+
+ TEST_START();
+
+ pft = pft_create(TBL_SIZE);
+ if (pft == NULL) {
+ printf("Failed to create.\n");
+ goto fail;
+ }
+
+ if (pft_add(pft, INT_TEST, fds, 3)) {
+ printf("Failed to insert.\n");
+ goto fail_pft;
+ }
+
+ pft_del_fd(pft, FD_GONE);
+
+ if (pft_lookup(pft, INT_TEST, &j, &len)) {
+ printf("Route with next hops left was dropped.\n");
+ goto fail_pft;
+ }
+
+ if (len != 2) {
+ printf("Expected 2 next hops, got %zu.\n", len);
+ goto fail_pft;
+ }
+
+ if (j[0] != FD_KEEP || j[1] != FD_OTHER) {
+ printf("Next hops not preserved in order (%d, %d).\n",
+ j[0], j[1]);
+ goto fail_pft;
+ }
+
+ pft_destroy(pft);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_pft:
+ pft_destroy(pft);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+static int test_pft_del_fd_untouched(void)
+{
+ struct pft * pft;
+ int * j;
+ size_t len;
+ int fds[] = {FD_KEEP};
+
+ TEST_START();
+
+ pft = pft_create(TBL_SIZE);
+ if (pft == NULL) {
+ printf("Failed to create.\n");
+ goto fail;
+ }
+
+ if (pft_add(pft, INT_TEST, fds, 1)) {
+ printf("Failed to insert.\n");
+ goto fail_pft;
+ }
+
+ pft_del_fd(pft, FD_GONE);
+
+ if (pft_lookup(pft, INT_TEST, &j, &len)) {
+ printf("Unrelated route was dropped.\n");
+ goto fail_pft;
+ }
+
+ if (len != 1 || *j != FD_KEEP) {
+ printf("Unrelated route was modified.\n");
+ goto fail_pft;
+ }
+
+ pft_destroy(pft);
+
+ TEST_SUCCESS();
+
+ return TEST_RC_SUCCESS;
+ fail_pft:
+ pft_destroy(pft);
+ fail:
+ TEST_FAIL();
+ return TEST_RC_FAIL;
+}
+
+int pft_test(int argc,
+ char ** argv)
+{
+ int ret = 0;
+
+ (void) argc;
+ (void) argv;
+
+ ret |= test_pft_create_destroy();
+ ret |= test_pft_insert_lookup();
+ ret |= test_pft_delete();
+ ret |= test_pft_del_fd_sole();
+ ret |= test_pft_del_fd_shared();
+ ret |= test_pft_del_fd_untouched();
+
+ return ret;
}