summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorDimitri Staessens <dimitri@ouroboros.rocks>2026-08-16 18:55:15 +0000
committerSander Vrijders <sander@ouroboros.rocks>2026-08-31 08:31:45 +0200
commit5c239c128c04883dbed6d66f574edf8b48d11e11 (patch)
tree6dfcc043c81bd10e1366172b9db5cf5ce93bb8d8 /src/tools
parenta830ed966eb3b7d6dbcc43e42a7b6b7c5d796c6a (diff)
downloadouroboros-5c239c128c04883dbed6d66f574edf8b48d11e11.tar.gz
ouroboros-5c239c128c04883dbed6d66f574edf8b48d11e11.zip
lib: Replace shim IPCPs with points of attachment
Removes the UDP and Ethernet shim IPCPs. The unicast and broadcast IPCPs can now directly attach to a "legacy" socket. We adopt Saltzer's Point-of-Attachment terminology, also advocated in Day's "Patterns in Network Architecture". The "poa" component manages these PoA's with one management thread, one link monitoring thread and one thread per attached point. For Ethernet PoA's the irm connect and enroll can resolve the destination IPCP or Layer name with a broadcast name query over the attached PoAs (first reply wins). UDP PoA's require a destination IP address or FQDN. attach to a local endpoint (required both server and client side): irm ipcp poa attach name a udp 10.0.0.1 irm ipcp poa attach name a udp 10.0.0.1:3435 irm ipcp poa attach name a udp [::1]:3435 irm ipcp poa attach name a eth dev eth0 irm ipcp poa attach name a eth dev eth0 ethertype 0xA000 release a PoA (refused while it carries a flow): irm ipcp poa detach name a udp 10.0.0.1:3435 irm ipcp poa detach name a eth eth0 list an IPCP's PoAs: irm ipcp poa list name a connect to a peer, by name or at an address: irm ipcp connect name b dst a irm ipcp connect name b dst a eth irm ipcp connect name b dst a eth dev eth0 irm ipcp connect name b dst a udp 10.0.0.1:3435 irm ipcp connect name b dst a udp peer.example.com:3435 disconnect by peer name, no address: irm ipcp disconnect name b dst a irm ipcp disconnect name b dst a component mgmt enroll has the same shape as connect: irm ipcp enroll name b layer lr autobind irm ipcp enroll name b layer lr autobind eth dev eth0 irm ipcp enroll name b layer lr autobind udp 10.0.0.1:3435 the IRMd config file attaches PoAs and names peers the same way: udp = [ "10.0.0.1", "10.0.0.1:3436" ] eth = [ "eth0", {dev="eth1", ethertype=0xA007} ] enrol={dst="LAN", eth={dev="eth0"}} conn=[{dst="lan3", eth={}}, {dst="lan4", udp="10.0.0.1:3435"}] Signed-off-by: Dimitri Staessens <dimitri@ouroboros.rocks> Signed-off-by: Sander Vrijders <sander@ouroboros.rocks>
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/CMakeLists.txt4
-rw-r--r--src/tools/irm/irm_ipcp.c2
-rw-r--r--src/tools/irm/irm_ipcp_bootstrap.c148
-rw-r--r--src/tools/irm/irm_ipcp_connect.c90
-rw-r--r--src/tools/irm/irm_ipcp_create.c15
-rw-r--r--src/tools/irm/irm_ipcp_enroll.c58
-rw-r--r--src/tools/irm/irm_ipcp_list.c23
-rw-r--r--src/tools/irm/irm_ipcp_poa.c98
-rw-r--r--src/tools/irm/irm_ipcp_poa_attach.c153
-rw-r--r--src/tools/irm/irm_ipcp_poa_detach.c153
-rw-r--r--src/tools/irm/irm_ipcp_poa_list.c136
-rw-r--r--src/tools/irm/irm_ops.h12
-rw-r--r--src/tools/irm/irm_utils.c233
-rw-r--r--src/tools/irm/irm_utils.h41
14 files changed, 972 insertions, 194 deletions
diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt
index 6b418838..bb601733 100644
--- a/src/tools/CMakeLists.txt
+++ b/src/tools/CMakeLists.txt
@@ -15,6 +15,10 @@ set(IRM_SOURCES
irm/irm_ipcp_destroy.c
irm/irm_ipcp_bootstrap.c
irm/irm_ipcp_enroll.c
+ irm/irm_ipcp_poa.c
+ irm/irm_ipcp_poa_attach.c
+ irm/irm_ipcp_poa_detach.c
+ irm/irm_ipcp_poa_list.c
irm/irm_ipcp_list.c
irm/irm_ipcp_connect.c
irm/irm_ipcp_disconnect.c
diff --git a/src/tools/irm/irm_ipcp.c b/src/tools/irm/irm_ipcp.c
index 34458a20..5c0db9cf 100644
--- a/src/tools/irm/irm_ipcp.c
+++ b/src/tools/irm/irm_ipcp.c
@@ -46,6 +46,7 @@ static void usage(void)
printf("Usage: irm ipcp [OPERATION]\n\n"
"where OPERATION in {create destroy\n"
" bootstrap enroll\n"
+ " poa\n"
" connect disconnect\n"
" list\n"
" help}\n");
@@ -68,6 +69,7 @@ static const struct cmd {
{ "destroy", do_destroy_ipcp },
{ "bootstrap", do_bootstrap_ipcp },
{ "enroll", do_enroll_ipcp },
+ { "poa", poa_cmd },
{ "connect", do_connect_ipcp },
{ "disconnect", do_disconnect_ipcp },
{ "list", do_list_ipcp},
diff --git a/src/tools/irm/irm_ipcp_bootstrap.c b/src/tools/irm/irm_ipcp_bootstrap.c
index cc8bf8fa..bbcf2312 100644
--- a/src/tools/irm/irm_ipcp_bootstrap.c
+++ b/src/tools/irm/irm_ipcp_bootstrap.c
@@ -53,10 +53,6 @@
#define UNICAST "unicast"
#define BROADCAST "broadcast"
-#define IP_UDP4 "udp4"
-#define IP_UDP6 "udp6"
-#define ETH_LLC "eth-llc"
-#define ETH_DIX "eth-dix"
#define LOCAL "local"
#define MD5 "MD5"
@@ -76,18 +72,13 @@
#define DT(x) default_dt_config.x
#define DHT(x) default_dht_config.params.x
#define UNI(x) default_uni_config.x
-#define DIX(x) eth_dix_default_conf.eth.x
-#define LLC(x) eth_llc_default_conf.eth.x
-#define UD4(x) udp4_default_conf.udp4.x
-#define UD6(x) udp6_default_conf.udp6.x
static char * usage_str = \
"Usage: irm ipcp bootstrap\n"
" name <ipcp name>\n"
" layer <layer name>\n"
" [type [TYPE]]\n"
- "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " "
- IP_UDP4 " " IP_UDP6 " " ETH_LLC " " ETH_DIX "},\n\n"
+ "where TYPE in {" UNICAST " " BROADCAST " " LOCAL "},\n\n"
"if TYPE == " UNICAST "\n"
" [addr_auth <ADDRESS_POLICY> (default: %s)]\n"
" [directory <DIRECTORY_POLICY> (default: %s)]\n"
@@ -117,28 +108,6 @@ static char * usage_str = \
" [ls_t_recalc <pff recalc interval (s)> (default: %ld)]\n"
" [ls_t_update <LSA update interval (s)> (default: %ld)]\n"
" [ls_t_timeo <link timeout (s)> (default: %ld)]\n\n"
- "if TYPE == " IP_UDP4 "\n"
- " ip <IP address in dotted notation>\n"
- " [port <UDP port> (default: %d)]\n"
- " [dns <DDNS IPv4 address in dotted notation>"
- " (default: none)]\n\n"
- "if TYPE == " IP_UDP6 "\n"
- " ip <IPv6 address>\n"
- " [port <UDP port> (default: %d)]\n"
- " [dns <DDNS IPv6 address>"
- " (default: none)]\n\n"
-
- "if TYPE == " ETH_LLC "\n"
- " dev <interface name>\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
- "if TYPE == " ETH_DIX "\n"
- " dev <interface name>\n"
- " [ethertype <ethertype> (default: 0x%4X)]\n"
- " [hash [ALGORITHM] (default: %s)]\n"
- "where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
- SHA3_384 " " SHA3_512 "}\n\n"
"if TYPE == " LOCAL "\n"
" [hash [ALGORITHM] (default: %s)]\n"
"where ALGORITHM in {" SHA3_224 " " SHA3_256 " "
@@ -160,15 +129,6 @@ static void usage(void)
/* ls */
default_ls_config.t_recalc, default_ls_config.t_update,
default_ls_config.t_timeo,
- /* udp4 */
- UD4(port),
- /* udp6 */
- UD6(port),
- /* eth_llc */
- SHA3_256,
- /* eth_dix */
- DIX(ethertype),
- SHA3_256,
/* local */
SHA3_256,
/* broadcast */
@@ -190,19 +150,9 @@ int do_bootstrap_ipcp(int argc,
enum pol_addr_auth addr_auth_type = UNI(addr_auth_type);
enum pol_cong_avoid cong_avoid = UNI(cong_avoid);
enum pol_dir_hash hash_algo = DIR_HASH_SHA3_256;
- char * ipstr = NULL;
- char * dnsstr = NULL;
- struct in_addr ip4_addr = {.s_addr = INADDR_ANY};
- struct in_addr dns4_addr = UD4(dns_addr);
- int port4 = UD4(port);
- struct in6_addr ip6_addr = IN6ADDR_ANY_INIT;
- struct in6_addr dns6_addr = UD6(dns_addr);
- int port6 = UD6(port);
char * ipcp_type = NULL;
enum ipcp_type type = IPCP_INVALID;
char * layer = NULL;
- char * dev = NULL;
- uint16_t ethertype = DIX(ethertype);
struct ipcp_list_info * ipcps;
ssize_t len = 0;
int i = 0;
@@ -211,6 +161,14 @@ int do_bootstrap_ipcp(int argc,
while (argc > 0) {
cargs = 2;
+ if (matches(*argv, "autobind") == 0)
+ cargs = 1;
+
+ if (argc < cargs) {
+ usage();
+ return -1;
+ }
+
if (matches(*argv, "type") == 0) {
ipcp_type = *(argv + 1);
} else if (matches(*argv, "layer") == 0) {
@@ -228,24 +186,6 @@ int do_bootstrap_ipcp(int argc,
hash_algo = DIR_HASH_SHA3_512;
else
goto unknown_param;
- } else if (matches(*argv, "ip") == 0) {
- ipstr = *(argv + 1);
- } else if (matches(*argv, "dns") == 0) {
- dnsstr = *(argv + 1);
- } else if (matches(*argv, "device") == 0) {
- dev = *(argv + 1);
- } else if (matches(*argv, "ethertype") == 0) {
- /* NOTE: We might do some more checks on strtol. */
- if (matches(*(argv + 1), "0x") == 0)
- ethertype = strtol(*(argv + 1), NULL, 0);
- else
- ethertype = strtol(*(argv + 1), NULL, 16);
- if (ethertype < 0x0600 || ethertype >= 0xFFFF) {
- printf("Invalid Ethertype: \"%s\".\n"
- "Recommended range: 0xA000-0xEFFF.\n",
- *(argv + 1));
- return -1;
- }
} else if (matches(*argv, "addr") == 0) {
addr_size = atoi(*(argv + 1));
} else if (matches(*argv, "eid") == 0) {
@@ -254,9 +194,6 @@ int do_bootstrap_ipcp(int argc,
max_ttl = atoi(*(argv + 1));
} else if (matches(*argv, "rtt") == 0) {
max_rtt = atoi(*(argv + 1));
- } else if (matches(*argv, "port") == 0) {
- port4 = atoi(*(argv + 1));
- port6 = port4;
} else if (matches(*argv, "autobind") == 0) {
autobind = true;
cargs = 1;
@@ -334,55 +271,11 @@ int do_bootstrap_ipcp(int argc,
type = IPCP_UNICAST;
else if (matches(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (matches(ipcp_type, IP_UDP4) == 0)
- type = IPCP_UDP4;
- else if (matches(ipcp_type, IP_UDP6) == 0)
- type = IPCP_UDP6;
- else if (matches(ipcp_type, ETH_DIX) == 0)
- type = IPCP_ETH_DIX;
- else if (matches(ipcp_type, ETH_LLC) == 0)
- type = IPCP_ETH_LLC;
else if (matches(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
else goto fail_usage;
}
- if (type == IPCP_UDP4) {
- if (inet_pton (AF_INET, ipstr, &ip4_addr) != 1) {
- printf("Invalid IPv4 address: \"%s\".\n", ipstr);
- goto fail_usage;
- }
-
- if (ip4_addr.s_addr == INADDR_ANY) {
- printf("Cannot use IPv4 address: \"%s\".\n", ipstr);
- goto fail_usage;
- }
-
- if (dnsstr != NULL &&
- inet_pton(AF_INET, dnsstr, &dns4_addr) != 1) {
- printf("Invalid DNS IPv4 address: \"%s\".\n", dnsstr);
- goto fail_usage;
- }
- }
-
- if (type == IPCP_UDP6) {
- if (inet_pton(AF_INET6, ipstr, &ip6_addr) != 1) {
- printf("Invalid IPv6 address: \"%s\".\n", ipstr);
- goto fail_usage;
- }
-
- if (IN6_IS_ADDR_UNSPECIFIED(&ip6_addr)) {
- printf("Cannot use IPv6 address: \"%s\".\n", ipstr);
- goto fail_usage;
- }
-
- if (dnsstr != NULL &&
- inet_pton(AF_INET6, dnsstr, &dns6_addr) != 1) {
- printf("Invalid DNS IPv6 address: \"%s\".\n", dnsstr);
- goto fail_usage;
- }
- }
-
if (pid == -1) {
if (ipcp_type == NULL) {
printf("No IPCPs matching %s found.\n\n", ipcp);
@@ -432,29 +325,6 @@ int do_bootstrap_ipcp(int argc,
conf.unicast.cong_avoid = cong_avoid;
conf.unicast.dir = dir_config;
break;
- case IPCP_UDP4:
- conf.udp4.ip_addr = ip4_addr;
- conf.udp4.dns_addr = dns4_addr;
- conf.udp4.port = port4;
- break;
- case IPCP_UDP6:
- conf.udp6.ip_addr = ip6_addr;
- conf.udp6.dns_addr = dns6_addr;
- conf.udp6.port = port6;
- break;
- case IPCP_ETH_DIX:
- conf.eth.ethertype = ethertype;
- /* FALLTHRU */
- case IPCP_ETH_LLC:
- if (dev == NULL)
- goto fail_usage;
- if (strlen(dev) > DEV_NAME_SIZE) {
- printf("Device name too long.\n\n");
- goto fail_usage;
- }
-
- strcpy(conf.eth.dev, dev);
- break;
case IPCP_BROADCAST:
/* FALLTHRU */
case IPCP_LOCAL:
diff --git a/src/tools/irm/irm_ipcp_connect.c b/src/tools/irm/irm_ipcp_connect.c
index fb21faec..66646e7d 100644
--- a/src/tools/irm/irm_ipcp_connect.c
+++ b/src/tools/irm/irm_ipcp_connect.c
@@ -44,6 +44,7 @@
#include <stdio.h>
#include <stdlib.h>
+#include <arpa/inet.h>
#include <string.h>
#define DT "dt"
@@ -55,10 +56,14 @@ static void usage(void)
" name <ipcp name>\n"
" dst <name of destination IPCP>\n"
" [component [COMPONENT]]\n"
- "where COMPONENT in {" DT " " MGMT "}\n\n"
+ " [udp [UDP_PEER]]\n"
+ " [eth [dev <device>]"
+ " [ethertype <ethertype>]]\n"
+ "where COMPONENT in {" DT " " MGMT "}\n"
+ "and UDP_PEER is <IP address or host>[:<port>]\n\n"
"if COMPONENT == " DT "\n"
- " [qos [QOS]\n"
- "where QOS in {raw, best, voice, video, data}\n");
+ " [qos [QOS]]\n"
+ "where QOS in {raw, safe, rt, rt-safe, msg}\n");
}
int do_connect_ipcp(int argc,
@@ -69,13 +74,29 @@ int do_connect_ipcp(int argc,
char * comp = "*";
char * component = NULL;
char * qos = NULL;
+ char * udpstr = NULL;
+ char * devstr = NULL;
+ uint16_t ethertype = POA_ETHERTYPE;
+ bool eth = false;
+ struct poa_addr addr;
+ struct poa_addr * pa = NULL;
struct ipcp_list_info * ipcps;
ssize_t len = 0;
pid_t pid = -1;
ssize_t i;
+ int cargs;
qosspec_t qs = qos_raw;
while (argc > 0) {
+ cargs = 2;
+ if (strcmp(*argv, "eth") == 0)
+ cargs = 1;
+
+ if (argc < cargs) {
+ usage();
+ return -1;
+ }
+
if (matches(*argv, "name") == 0) {
ipcp = *(argv + 1);
} else if (matches(*argv, "dst") == 0) {
@@ -84,21 +105,66 @@ int do_connect_ipcp(int argc,
comp = *(argv + 1);
} else if (matches(*argv, "qos") == 0) {
qos = *(argv + 1);
+ } else if (strcmp(*argv, "udp") == 0) {
+ udpstr = *(argv + 1);
+ } else if (strcmp(*argv, "dev") == 0) {
+ devstr = *(argv + 1);
+ } else if (strcmp(*argv, "ethertype") == 0) {
+ if (parse_ethertype(*(argv + 1), &ethertype) < 0) {
+ printf("Invalid ethertype: \"%s\".\n",
+ *(argv + 1));
+ return -1;
+ }
+ } else if (strcmp(*argv, "eth") == 0) {
+ eth = true;
+ cargs = 1;
} else {
printf("\"%s\" is unknown, try \"irm "
"ipcp connect\".\n", *argv);
return -1;
}
- argc -= 2;
- argv += 2;
+ argc -= cargs;
+ argv += cargs;
}
- if (ipcp == NULL || dst == NULL || comp == NULL) {
+ if (ipcp == NULL || comp == NULL) {
usage();
return -1;
}
+ memset(&addr, 0, sizeof(addr));
+
+ if (udpstr != NULL) {
+ if (poa_addr_set_udp(&addr, udpstr) < 0)
+ return -1;
+ pa = &addr;
+ }
+
+ if (eth) {
+ if (udpstr != NULL) {
+ printf("Connect via udp or eth, not both.\n");
+ return -1;
+ }
+
+ if (dst == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (poa_addr_set_eth(&addr, devstr, ethertype) < 0)
+ return -1;
+ pa = &addr;
+ }
+
+ if (dst == NULL && pa == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (dst == NULL)
+ dst = "";
+
if (qos != NULL) {
if (strcmp(qos, "raw") == 0)
qs = qos_raw;
@@ -110,9 +176,11 @@ int do_connect_ipcp(int argc,
qs = qos_rt_safe;
else if (strcmp(qos, "msg") == 0)
qs = qos_msg;
- else if (strcmp(qos, "stream") == 0)
- qs = qos_stream;
- else
+ else if (strcmp(qos, "stream") == 0) {
+ printf("Stream QoS is not allowed on "
+ "IPCP component flows.\n");
+ return -1;
+ } else
printf("Unknown QoS cube, defaulting to raw.\n");
}
@@ -129,13 +197,13 @@ int do_connect_ipcp(int argc,
if (wildcard_match(comp, MGMT) == 0) {
component = MGMT_COMP;
/* FIXME: move to qos_msg when stable */
- if (irm_connect_ipcp(pid, dst, component, qos_raw))
+ if (irm_connect_ipcp(pid, dst, component, qos_raw, pa))
return -1;
}
if (wildcard_match(comp, DT) == 0) {
component = DT_COMP;
- if (irm_connect_ipcp(pid, dst, component, qs))
+ if (irm_connect_ipcp(pid, dst, component, qs, pa))
return -1;
}
diff --git a/src/tools/irm/irm_ipcp_create.c b/src/tools/irm/irm_ipcp_create.c
index c6b2074b..15cfedde 100644
--- a/src/tools/irm/irm_ipcp_create.c
+++ b/src/tools/irm/irm_ipcp_create.c
@@ -46,10 +46,6 @@
#define UNICAST "unicast"
#define BROADCAST "broadcast"
-#define UDP4 "udp4"
-#define UDP6 "udp6"
-#define ETH_LLC "eth-llc"
-#define ETH_DIX "eth-dix"
#define LOCAL "local"
static void usage(void)
@@ -57,8 +53,7 @@ static void usage(void)
printf("Usage: irm ipcp create\n"
" name <ipcp name>\n"
" type [TYPE]\n\n"
- "where TYPE in {" UNICAST " " BROADCAST " " LOCAL " "
- UDP4 " " UDP6 " " ETH_LLC " " ETH_DIX "}\n");
+ "where TYPE in {" UNICAST " " BROADCAST " " LOCAL "}\n");
}
int do_create_ipcp(int argc,
@@ -93,16 +88,8 @@ int do_create_ipcp(int argc,
type = IPCP_UNICAST;
else if (strcmp(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (strcmp(ipcp_type, UDP4) == 0)
- type = IPCP_UDP4;
- else if (strcmp(ipcp_type, UDP6) == 0)
- type = IPCP_UDP6;
else if (strcmp(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
- else if (strcmp(ipcp_type, ETH_LLC) == 0)
- type = IPCP_ETH_LLC;
- else if (strcmp(ipcp_type, ETH_DIX) == 0)
- type = IPCP_ETH_DIX;
else {
printf("IPCP type \"%s\" is unknown.\n", ipcp_type);
usage();
diff --git a/src/tools/irm/irm_ipcp_enroll.c b/src/tools/irm/irm_ipcp_enroll.c
index 350b536e..54fed022 100644
--- a/src/tools/irm/irm_ipcp_enroll.c
+++ b/src/tools/irm/irm_ipcp_enroll.c
@@ -44,6 +44,7 @@
#include "irm_ops.h"
#include "irm_utils.h"
+#include <arpa/inet.h>
#include <string.h>
#define UNICAST "unicast"
@@ -57,7 +58,10 @@ static void usage(void)
" [dst <destination to enroll with>]\n"
" [type [TYPE], default = " UNICAST "]\n"
" [autobind]\n"
- "where TYPE in {" UNICAST " " BROADCAST "}\n");
+ " [udp [UDP_PEER]]\n"
+ " [eth [dev <device>] [ethertype <ethertype>]]\n"
+ "where TYPE in {" UNICAST " " BROADCAST "}\n"
+ "and UDP_PEER is <IP address or host>[:<port>]\n");
}
static int get_layer_name(const char * ipcp,
@@ -86,6 +90,12 @@ int do_enroll_ipcp(int argc,
char * ipcp = NULL;
char * layer = NULL;
char * dst = NULL;
+ char * udpstr = NULL;
+ char * devstr = NULL;
+ uint16_t ethertype = POA_ETHERTYPE;
+ bool eth = false;
+ struct poa_addr addr;
+ struct poa_addr * pa = NULL;
struct ipcp_list_info * ipcps;
pid_t pid = -1;
ssize_t len = 0;
@@ -97,14 +107,37 @@ int do_enroll_ipcp(int argc,
while (argc > 0) {
cargs = 2;
+ if (strcmp(*argv, "eth") == 0)
+ cargs = 1;
+ else if (matches(*argv, "autobind") == 0)
+ cargs = 1;
+
+ if (argc < cargs) {
+ usage();
+ return -1;
+ }
+
if (matches(*argv, "name") == 0) {
ipcp = *(argv + 1);
} else if (matches(*argv, "type") == 0) {
ipcp_type = *(argv + 1);
} else if (matches(*argv, "layer") == 0) {
layer = *(argv + 1);
- } else if (matches(*argv, "dst") == 0) {
+ } else if (strcmp(*argv, "dst") == 0) {
dst = *(argv + 1);
+ } else if (strcmp(*argv, "udp") == 0) {
+ udpstr = *(argv + 1);
+ } else if (strcmp(*argv, "dev") == 0) {
+ devstr = *(argv + 1);
+ } else if (strcmp(*argv, "ethertype") == 0) {
+ if (parse_ethertype(*(argv + 1), &ethertype) < 0) {
+ printf("Invalid ethertype: \"%s\".\n",
+ *(argv + 1));
+ return -1;
+ }
+ } else if (strcmp(*argv, "eth") == 0) {
+ eth = true;
+ cargs = 1;
} else if (matches(*argv, "autobind") == 0) {
autobind = true;
cargs = 1;
@@ -126,6 +159,25 @@ int do_enroll_ipcp(int argc,
if (dst == NULL)
dst = layer;
+ memset(&addr, 0, sizeof(addr));
+
+ if (udpstr != NULL) {
+ if (poa_addr_set_udp(&addr, udpstr) < 0)
+ return -1;
+ pa = &addr;
+ }
+
+ if (eth) {
+ if (udpstr != NULL) {
+ printf("Enroll via udp or eth, not both.\n");
+ return -1;
+ }
+
+ if (poa_addr_set_eth(&addr, devstr, ethertype) < 0)
+ return -1;
+ pa = &addr;
+ }
+
if (strcmp(ipcp_type, UNICAST) == 0)
type = IPCP_UNICAST;
else if (strcmp(ipcp_type, BROADCAST) == 0)
@@ -155,7 +207,7 @@ int do_enroll_ipcp(int argc,
pid = ipcps[i].pid;
- if (irm_enroll_ipcp(pid, dst)) {
+ if (irm_enroll_ipcp(pid, dst, pa) < 0) {
printf("Failed to enroll IPCP.\n");
goto fail;
}
diff --git a/src/tools/irm/irm_ipcp_list.c b/src/tools/irm/irm_ipcp_list.c
index a211a02b..60154efb 100644
--- a/src/tools/irm/irm_ipcp_list.c
+++ b/src/tools/irm/irm_ipcp_list.c
@@ -48,10 +48,6 @@
#define UNICAST "unicast"
#define BROADCAST "broadcast"
-#define UDP4 "udp4"
-#define UDP6 "udp6"
-#define ETH_LLC "eth-llc"
-#define ETH_DIX "eth-dix"
#define LOCAL "local"
static void usage(void)
@@ -60,8 +56,7 @@ static void usage(void)
" [name <ipcp name>]\n"
" [layer <layer_name>]\n\n"
" [type [TYPE]]\n\n"
- "where TYPE = {" UNICAST " " LOCAL " "
- UDP4 " " UDP6 " " ETH_LLC " " ETH_DIX "}\n");
+ "where TYPE = {" UNICAST " " BROADCAST " " LOCAL "}\n");
}
static char * str_type(enum ipcp_type type)
@@ -71,14 +66,6 @@ static char * str_type(enum ipcp_type type)
return UNICAST;
case IPCP_BROADCAST:
return BROADCAST;
- case IPCP_ETH_LLC:
- return ETH_LLC;
- case IPCP_ETH_DIX:
- return ETH_DIX;
- case IPCP_UDP4:
- return UDP4;
- case IPCP_UDP6:
- return UDP6;
case IPCP_LOCAL:
return LOCAL;
default:
@@ -116,16 +103,8 @@ int do_list_ipcp(int argc,
type = IPCP_UNICAST;
else if (strcmp(ipcp_type, BROADCAST) == 0)
type = IPCP_BROADCAST;
- else if (strcmp(ipcp_type, UDP4) == 0)
- type = IPCP_UDP4;
- else if (strcmp(ipcp_type, UDP6) == 0)
- type = IPCP_UDP6;
else if (strcmp(ipcp_type, LOCAL) == 0)
type = IPCP_LOCAL;
- else if (strcmp(ipcp_type, ETH_LLC) == 0)
- type = IPCP_ETH_LLC;
- else if (strcmp(ipcp_type, ETH_DIX) == 0)
- type = IPCP_ETH_DIX;
else {
usage();
return -1;
diff --git a/src/tools/irm/irm_ipcp_poa.c b/src/tools/irm/irm_ipcp_poa.c
new file mode 100644
index 00000000..cd939020
--- /dev/null
+++ b/src/tools/irm/irm_ipcp_poa.c
@@ -0,0 +1,98 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Connect components of unicast or broadcast IPC processes
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "irm_ops.h"
+#include "irm_utils.h"
+
+#include <stdio.h>
+#include <string.h>
+
+static void usage(void)
+{
+ printf("Usage: irm ipcp poa [OPERATION]\n\n"
+ "where OPERATION in {attach detach list help}\n");
+}
+
+static int do_help(int argc,
+ char ** argv)
+{
+ (void) argc;
+ (void) argv;
+
+ usage();
+
+ return 0;
+}
+
+static const struct cmd {
+ const char * cmd;
+ int (* func)(int argc, char ** argv);
+} cmds[] = {
+ { "attach", do_attach_ipcp },
+ { "detach", do_detach_ipcp },
+ { "list", do_list_poas },
+ { "help", do_help },
+ { NULL, NULL }
+};
+
+static int do_cmd(const char * argv0,
+ int argc,
+ char ** argv)
+{
+ const struct cmd * c;
+
+ for (c = cmds; c->cmd != NULL; ++c)
+ if (matches(argv0, c->cmd) == 0)
+ return c->func(argc - 1, argv + 1);
+
+ fprintf(stderr, "\"%s\" is unknown, try \"irm ipcp poa help\".\n",
+ argv0);
+
+ return -1;
+}
+
+int poa_cmd(int argc,
+ char ** argv)
+{
+ if (argc < 1) {
+ usage();
+ return -1;
+ }
+
+ return do_cmd(argv[0], argc, argv);
+}
diff --git a/src/tools/irm/irm_ipcp_poa_attach.c b/src/tools/irm/irm_ipcp_poa_attach.c
new file mode 100644
index 00000000..74a83344
--- /dev/null
+++ b/src/tools/irm/irm_ipcp_poa_attach.c
@@ -0,0 +1,153 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Connect components of unicast or broadcast IPC processes
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ouroboros/irm.h>
+
+#include "irm_ops.h"
+#include "irm_utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void usage(void)
+{
+ printf("Usage: irm ipcp poa attach\n"
+ " name <ipcp name>\n"
+ " [udp [UDP_POA]]\n"
+ " [eth [ETH_POA]]\n"
+ "where exactly one of udp or eth is given\n"
+ "and UDP_POA is <local IP address>[:<port>]\n"
+ "and ETH_POA is dev <device> [ethertype <ethertype>]\n");
+}
+
+int do_attach_ipcp(int argc,
+ char ** argv)
+{
+ char * ipcp = NULL;
+ char * udpstr = NULL;
+ char * devstr = NULL;
+ uint16_t ethertype = POA_ETHERTYPE;
+ bool eth = false;
+ struct poa_spec poa;
+ char str[POA_STRLEN + 1];
+ struct ipcp_list_info * ipcps;
+ ssize_t len = 0;
+ pid_t pid = -1;
+ ssize_t i;
+ int cargs;
+
+ while (argc > 0) {
+ cargs = 2;
+
+ if (strcmp(*argv, "eth") == 0)
+ cargs = 1;
+
+ if (argc < cargs) {
+ usage();
+ return -1;
+ }
+
+ if (matches(*argv, "name") == 0) {
+ ipcp = *(argv + 1);
+ } else if (strcmp(*argv, "udp") == 0) {
+ udpstr = *(argv + 1);
+ } else if (strcmp(*argv, "dev") == 0) {
+ devstr = *(argv + 1);
+ } else if (strcmp(*argv, "ethertype") == 0) {
+ if (parse_ethertype(*(argv + 1), &ethertype) < 0) {
+ printf("Invalid ethertype: \"%s\".\n",
+ *(argv + 1));
+ return -1;
+ }
+ } else if (strcmp(*argv, "eth") == 0) {
+ eth = true;
+ cargs = 1;
+ } else {
+ printf("\"%s\" is unknown, try \"irm ipcp "
+ "poa attach\".\n", *argv);
+ return -1;
+ }
+
+ argc -= cargs;
+ argv += cargs;
+ }
+
+ if (ipcp == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (eth && udpstr != NULL) {
+ printf("A PoA is udp or eth, not both.\n");
+ return -1;
+ }
+
+ if (eth && devstr == NULL) {
+ printf("An eth PoA needs a device.\n");
+ return -1;
+ }
+
+ if (!eth && udpstr == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (poa_spec_set(&poa, udpstr, devstr, ethertype) < 0)
+ return -1;
+
+ len = irm_list_ipcps(&ipcps);
+ for (i = 0; i < len; i++)
+ if (strcmp(ipcps[i].name, ipcp) == 0)
+ pid = ipcps[i].pid;
+
+ free(ipcps);
+
+ if (pid == -1) {
+ printf("No such IPCP: \"%s\".\n", ipcp);
+ return -1;
+ }
+
+ if (irm_attach_ipcp(pid, &poa) < 0) {
+ poa_spec_str(&poa, str, sizeof(str));
+ printf("Failed to attach PoA %s on IPCP %s.\n", str, ipcp);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/tools/irm/irm_ipcp_poa_detach.c b/src/tools/irm/irm_ipcp_poa_detach.c
new file mode 100644
index 00000000..ce6bef13
--- /dev/null
+++ b/src/tools/irm/irm_ipcp_poa_detach.c
@@ -0,0 +1,153 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * Connect components of unicast or broadcast IPC processes
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ouroboros/irm.h>
+
+#include "irm_ops.h"
+#include "irm_utils.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void usage(void)
+{
+ printf("Usage: irm ipcp poa detach\n"
+ " name <ipcp name>\n"
+ " [udp [UDP_POA]]\n"
+ " [eth [ETH_POA]]\n"
+ "where exactly one of udp or eth is given\n"
+ "and UDP_POA is <local IP address>[:<port>]\n"
+ "and ETH_POA is dev <device> [ethertype <ethertype>]\n");
+}
+
+int do_detach_ipcp(int argc,
+ char ** argv)
+{
+ char * ipcp = NULL;
+ char * udpstr = NULL;
+ char * devstr = NULL;
+ uint16_t ethertype = POA_ETHERTYPE;
+ bool eth = false;
+ struct poa_spec poa;
+ char str[POA_STRLEN + 1];
+ struct ipcp_list_info * ipcps;
+ ssize_t len = 0;
+ pid_t pid = -1;
+ ssize_t i;
+ int cargs;
+
+ while (argc > 0) {
+ cargs = 2;
+
+ if (strcmp(*argv, "eth") == 0)
+ cargs = 1;
+
+ if (argc < cargs) {
+ usage();
+ return -1;
+ }
+
+ if (matches(*argv, "name") == 0) {
+ ipcp = *(argv + 1);
+ } else if (strcmp(*argv, "udp") == 0) {
+ udpstr = *(argv + 1);
+ } else if (strcmp(*argv, "dev") == 0) {
+ devstr = *(argv + 1);
+ } else if (strcmp(*argv, "ethertype") == 0) {
+ if (parse_ethertype(*(argv + 1), &ethertype) < 0) {
+ printf("Invalid ethertype: \"%s\".\n",
+ *(argv + 1));
+ return -1;
+ }
+ } else if (strcmp(*argv, "eth") == 0) {
+ eth = true;
+ cargs = 1;
+ } else {
+ printf("\"%s\" is unknown, try \"irm ipcp "
+ "poa detach\".\n", *argv);
+ return -1;
+ }
+
+ argc -= cargs;
+ argv += cargs;
+ }
+
+ if (ipcp == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (eth && udpstr != NULL) {
+ printf("A PoA is udp or eth, not both.\n");
+ return -1;
+ }
+
+ if (eth && devstr == NULL) {
+ printf("An eth PoA needs a device.\n");
+ return -1;
+ }
+
+ if (!eth && udpstr == NULL) {
+ usage();
+ return -1;
+ }
+
+ if (poa_spec_set(&poa, udpstr, devstr, ethertype) < 0)
+ return -1;
+
+ len = irm_list_ipcps(&ipcps);
+ for (i = 0; i < len; i++)
+ if (strcmp(ipcps[i].name, ipcp) == 0)
+ pid = ipcps[i].pid;
+
+ free(ipcps);
+
+ if (pid == -1) {
+ printf("No such IPCP: \"%s\".\n", ipcp);
+ return -1;
+ }
+
+ if (irm_detach_ipcp(pid, &poa) < 0) {
+ poa_spec_str(&poa, str, sizeof(str));
+ printf("Failed to detach PoA %s on IPCP %s.\n", str, ipcp);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/tools/irm/irm_ipcp_poa_list.c b/src/tools/irm/irm_ipcp_poa_list.c
new file mode 100644
index 00000000..8797c3c1
--- /dev/null
+++ b/src/tools/irm/irm_ipcp_poa_list.c
@@ -0,0 +1,136 @@
+/*
+ * Ouroboros - Copyright (C) 2016 - 2026
+ *
+ * List the points of attachment of an IPC process
+ *
+ * Dimitri Staessens <dimitri@ouroboros.rocks>
+ * Sander Vrijders <sander@ouroboros.rocks>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ouroboros/irm.h>
+
+#include "irm_ops.h"
+#include "irm_utils.h"
+
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static void usage(void)
+{
+ printf("Usage: irm ipcp poa list\n"
+ " name <ipcp name>\n");
+}
+
+static void print_poa(const struct poa_spec * poa)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ switch (poa->type) {
+ case POA_UDP4:
+ if (inet_ntop(AF_INET, &poa->udp4.ip_addr, buf,
+ sizeof(buf)) == NULL)
+ return;
+ printf("%-6s %s:%u\n", "udp4", buf, poa->udp4.port);
+ break;
+ case POA_UDP6:
+ if (inet_ntop(AF_INET6, &poa->udp6.ip_addr, buf,
+ sizeof(buf)) == NULL)
+ return;
+ printf("%-6s [%s]:%u\n", "udp6", buf, poa->udp6.port);
+ break;
+ case POA_ETH:
+ printf("%-6s %s 0x%04X %02x:%02x:%02x:%02x:%02x:%02x\n",
+ "eth", poa->eth.dev, poa->eth.ethertype,
+ poa->eth.mac[0], poa->eth.mac[1], poa->eth.mac[2],
+ poa->eth.mac[3], poa->eth.mac[4], poa->eth.mac[5]);
+ break;
+ default:
+ break;
+ }
+}
+
+int do_list_poas(int argc,
+ char ** argv)
+{
+ char * ipcp = NULL;
+ struct poa_spec * poas = NULL;
+ struct ipcp_list_info * ipcps;
+ ssize_t len = 0;
+ pid_t pid = -1;
+ ssize_t n;
+ ssize_t i;
+
+ while (argc > 1) {
+ if (matches(*argv, "name") == 0) {
+ ipcp = *(argv + 1);
+ } else {
+ printf("\"%s\" is unknown, try \"irm ipcp "
+ "poa list\".\n", *argv);
+ return -1;
+ }
+
+ argc -= 2;
+ argv += 2;
+ }
+
+ if (ipcp == NULL) {
+ usage();
+ return -1;
+ }
+
+ len = irm_list_ipcps(&ipcps);
+ for (i = 0; i < len; i++)
+ if (strcmp(ipcps[i].name, ipcp) == 0)
+ pid = ipcps[i].pid;
+
+ free(ipcps);
+
+ if (pid == -1) {
+ printf("No such IPCP: \"%s\".\n", ipcp);
+ return -1;
+ }
+
+ n = irm_list_poas(pid, &poas);
+ if (n < 0) {
+ printf("Failed to list PoAs of %s.\n", ipcp);
+ return -1;
+ }
+
+ for (i = 0; i < n; i++)
+ print_poa(&poas[i]);
+
+ free(poas);
+
+ return 0;
+}
diff --git a/src/tools/irm/irm_ops.h b/src/tools/irm/irm_ops.h
index 195c5cbc..7f6c65cd 100644
--- a/src/tools/irm/irm_ops.h
+++ b/src/tools/irm/irm_ops.h
@@ -51,6 +51,18 @@ int do_bootstrap_ipcp(int argc,
int do_enroll_ipcp(int argc,
char ** argv);
+int poa_cmd(int argc,
+ char ** argv);
+
+int do_attach_ipcp(int argc,
+ char ** argv);
+
+int do_detach_ipcp(int argc,
+ char ** argv);
+
+int do_list_poas(int argc,
+ char ** argv);
+
int do_connect_ipcp(int argc,
char ** argv);
diff --git a/src/tools/irm/irm_utils.c b/src/tools/irm/irm_utils.c
index 69873097..c43accec 100644
--- a/src/tools/irm/irm_utils.c
+++ b/src/tools/irm/irm_utils.c
@@ -77,10 +77,21 @@
*/
-#include <string.h>
+#if defined(__linux__) || defined(__CYGWIN__)
+#define _DEFAULT_SOURCE
+#else
+#define _POSIX_C_SOURCE 200809L
+#endif
+
+#include <ouroboros/ipcp.h>
#include "irm_utils.h"
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
int matches(const char * cmd,
const char * pattern)
{
@@ -123,3 +134,223 @@ int wildcard_match(const char * pattern,
}
}
}
+
+/* Splits "<addr>[:<port>]"; bare IPv6 needs no brackets. */
+static int parse_udp_str(const char * str,
+ char * host,
+ int * port)
+{
+ struct in6_addr v6;
+ char buf[POA_HOST_STRLEN + 1];
+ char * p;
+ char * end;
+ long n;
+
+ *port = POA_UDP_PORT;
+
+ if (strlen(str) > POA_HOST_STRLEN)
+ goto fail;
+
+ strcpy(buf, str);
+
+ if (buf[0] == '[') {
+ p = strchr(buf, ']');
+ if (p == NULL)
+ goto fail;
+
+ *p++ = '\0';
+
+ strcpy(host, buf + 1);
+
+ if (*p == '\0')
+ return 0;
+
+ if (*p != ':')
+ goto fail;
+
+ ++p;
+ } else if (inet_pton(AF_INET6, buf, &v6) == 1) {
+ strcpy(host, buf);
+ return 0;
+ } else {
+ p = strrchr(buf, ':');
+ if (p == NULL) {
+ strcpy(host, buf);
+ return 0;
+ }
+
+ *p++ = '\0';
+
+ strcpy(host, buf);
+ }
+
+ n = strtol(p, &end, 10);
+ if (*p == '\0' || *end != '\0' || n < 1 || n > 65535)
+ goto fail;
+
+ *port = (int) n;
+
+ return 0;
+ fail:
+ printf("Invalid UDP address: \"%s\".\n", str);
+ return -1;
+}
+
+/* An unresolved name is left for the IRMd, which picks the family. */
+int poa_addr_set_udp(struct poa_addr * addr,
+ const char * str)
+{
+ char host[POA_HOST_STRLEN + 1];
+ int port;
+
+ if (parse_udp_str(str, host, &port) < 0)
+ return -1;
+
+ if (inet_pton(AF_INET, host, &addr->udp4.ip_addr) == 1) {
+ addr->type = POA_UDP4;
+ addr->udp4.port = port;
+ return 0;
+ }
+
+ if (inet_pton(AF_INET6, host, &addr->udp6.ip_addr) == 1) {
+ addr->type = POA_UDP6;
+ addr->udp6.port = port;
+ return 0;
+ }
+
+ addr->type = POA_UDP;
+ addr->udp4.port = port;
+
+ strcpy(addr->hostname, host);
+
+ return 0;
+}
+
+/*
+ * Parses a hex ethertype; rejects garbage and out-of-range values.
+ * Overflow clamps to LONG_MAX and lands in the range check.
+ */
+int parse_ethertype(const char * str,
+ uint16_t * ethertype)
+{
+ char * end;
+ long val;
+
+ val = strtol(str, &end, 16);
+
+ if (end == str || *end != '\0')
+ return -1;
+
+ if (val < 0 || val > 0xFFFF)
+ return -1;
+
+ *ethertype = (uint16_t) val;
+
+ return 0;
+}
+
+int poa_addr_set_eth(struct poa_addr * addr,
+ const char * devstr,
+ uint16_t ethertype)
+{
+ addr->type = POA_ETH;
+
+ addr->eth.src.ethertype = ethertype;
+ addr->eth.dst.ethertype = ethertype;
+
+ if (devstr != NULL) {
+ if (strlen(devstr) > DEV_NAME_SIZE) {
+ printf("Invalid device name: \"%s\".\n", devstr);
+ return -1;
+ }
+
+ strcpy(addr->eth.src.dev, devstr);
+ }
+
+ return 0;
+}
+
+/* Matches src/ipcpd/ipcp.c; keep in sync. */
+void poa_spec_str(const struct poa_spec * poa,
+ char * buf,
+ size_t len)
+{
+ char addr[INET6_ADDRSTRLEN];
+
+ switch (poa->type) {
+ case POA_UDP4:
+ if (inet_ntop(AF_INET, &poa->udp4.ip_addr,
+ addr, sizeof(addr)) == NULL)
+ break;
+
+ snprintf(buf, len, "udp4 %s:%u", addr, poa->udp4.port);
+ return;
+ case POA_UDP6:
+ if (inet_ntop(AF_INET6, &poa->udp6.ip_addr,
+ addr, sizeof(addr)) == NULL)
+ break;
+
+ snprintf(buf, len, "udp6 [%s]:%u", addr, poa->udp6.port);
+ return;
+ case POA_ETH:
+ snprintf(buf, len, "eth %s 0x%04X", poa->eth.dev,
+ poa->eth.ethertype);
+ return;
+ default:
+ break;
+ }
+
+ snprintf(buf, len, "(unknown)");
+}
+
+int poa_spec_set(struct poa_spec * poa,
+ const char * udpstr,
+ const char * devstr,
+ uint16_t ethertype)
+{
+ char host[POA_HOST_STRLEN + 1];
+ int port;
+
+ memset(poa, 0, sizeof(*poa));
+
+ if ((udpstr != NULL) + (devstr != NULL) > 1) {
+ printf("A PoA is an address or a device.\n");
+ return -1;
+ }
+
+ if (udpstr != NULL) {
+ if (parse_udp_str(udpstr, host, &port) < 0)
+ return -1;
+
+ if (inet_pton(AF_INET, host, &poa->udp4.ip_addr) == 1) {
+ poa->type = POA_UDP4;
+ poa->udp4.port = port;
+ return 0;
+ }
+
+ if (inet_pton(AF_INET6, host, &poa->udp6.ip_addr) == 1) {
+ poa->type = POA_UDP6;
+ poa->udp6.port = port;
+ return 0;
+ }
+
+ printf("Invalid IP address: \"%s\".\n", udpstr);
+ return -1;
+ }
+
+ if (devstr != NULL) {
+ if (strlen(devstr) > DEV_NAME_SIZE) {
+ printf("Invalid device name: \"%s\".\n", devstr);
+ return -1;
+ }
+
+ poa->type = POA_ETH;
+ poa->eth.ethertype = ethertype;
+
+ strcpy(poa->eth.dev, devstr);
+
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/src/tools/irm/irm_utils.h b/src/tools/irm/irm_utils.h
index c6d4bf18..3760b4e4 100644
--- a/src/tools/irm/irm_utils.h
+++ b/src/tools/irm/irm_utils.h
@@ -79,10 +79,43 @@
#ifndef OUROBOROS_TOOLS_IRM_UTILS_H
#define OUROBOROS_TOOLS_IRM_UTILS_H
-int matches(const char * cmd,
- const char * pattern);
+#include <ouroboros/ipcp.h>
-int wildcard_match(const char * pattern,
- const char * string);
+#include <arpa/inet.h>
+
+#include <stdint.h>
+
+int matches(const char * cmd,
+ const char * pattern);
+
+int wildcard_match(const char * pattern,
+ const char * string);
+
+/* Fill one PoA to attach or detach: an address or a device. */
+int poa_spec_set(struct poa_spec * poa,
+ const char * udpstr,
+ const char * devstr,
+ uint16_t ethertype);
+
+/* Fill a peer PoA address; a host name is resolved by the IRMd. */
+int poa_addr_set_udp(struct poa_addr * addr,
+ const char * str);
+
+/* Fits "eth <dev> 0x<type>", the longest PoA rendering. */
+/* Matches src/ipcpd/ipcp.c; keep in sync. */
+#define POA_STRLEN (DEV_NAME_SIZE + 11)
+
+/* Names a PoA the way the IPCP logs it. */
+void poa_spec_str(const struct poa_spec * poa,
+ char * buf,
+ size_t len);
+
+int poa_addr_set_eth(struct poa_addr * addr,
+ const char * devstr,
+ uint16_t ethertype);
+
+/* Parses a hex ethertype; rejects garbage and out-of-range values. */
+int parse_ethertype(const char * str,
+ uint16_t * ethertype);
#endif /* OUROBOROS_TOOLS_IRM_UTILS_H */