summaryrefslogtreecommitdiff
path: root/src/irmd/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/irmd/main.c')
-rw-r--r--src/irmd/main.c610
1 files changed, 577 insertions, 33 deletions
diff --git a/src/irmd/main.c b/src/irmd/main.c
index 19be4ab9..e587a552 100644
--- a/src/irmd/main.c
+++ b/src/irmd/main.c
@@ -61,6 +61,7 @@
#include <dirent.h>
#include <grp.h>
+#include <netdb.h>
#include <pwd.h>
#include <signal.h>
#include <spawn.h>
@@ -225,18 +226,6 @@ static pid_t spawn_ipcp(struct ipcp_info * info)
case IPCP_BROADCAST:
exec_name = IPCP_BROADCAST_EXEC;
break;
- case IPCP_UDP4:
- exec_name = IPCP_UDP4_EXEC;
- break;
- case IPCP_UDP6:
- exec_name = IPCP_UDP6_EXEC;
- break;
- case IPCP_ETH_LLC:
- exec_name = IPCP_ETH_LLC_EXEC;
- break;
- case IPCP_ETH_DIX:
- exec_name = IPCP_ETH_DIX_EXEC;
- break;
case IPCP_LOCAL:
exec_name = IPCP_LOCAL_EXEC;
break;
@@ -367,9 +356,6 @@ int bootstrap_ipcp(pid_t pid,
goto fail;
}
- if (conf->type == IPCP_UDP4 || conf->type == IPCP_UDP6)
- conf->layer_info.dir_hash_algo = (enum pol_dir_hash) HASH_MD5;
-
if (ipcp_bootstrap(pid, conf, &layer)) {
log_err("Could not bootstrap IPCP.");
goto fail;
@@ -389,8 +375,55 @@ int bootstrap_ipcp(pid_t pid,
return -1;
}
-int enroll_ipcp(pid_t pid,
- const char * dst)
+ssize_t list_poas(pid_t pid,
+ struct poa_spec ** eps)
+{
+ struct ipcp_info info;
+
+ info.pid = pid;
+ if (reg_get_ipcp(&info, NULL) < 0) {
+ log_err("Could not find IPCP %d.", pid);
+ return -1;
+ }
+
+ if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST)
+ return 0; /* nothing it could be attached to */
+
+ return ipcp_list_poas(pid, eps);
+}
+
+/* Only the types that can carry a PoA may take one. */
+int attach_ipcp(pid_t pid,
+ const struct poa_spec * poa,
+ bool attach)
+{
+ struct ipcp_info info;
+
+ info.pid = pid;
+ if (reg_get_ipcp(&info, NULL) < 0) {
+ log_err("Could not find IPCP %d.", pid);
+ return -1;
+ }
+
+ if (info.type != IPCP_UNICAST && info.type != IPCP_BROADCAST) {
+ log_err("IPCP %d does not support PoAs.", pid);
+ return -1;
+ }
+
+ if (ipcp_attach(pid, poa, attach) < 0) {
+ log_err("Could not %s IPCP %d.",
+ attach ? "attach" : "detach", pid);
+ return -1;
+ }
+
+ log_info("%s IPCP %d.", attach ? "Attached" : "Detached", pid);
+
+ return 0;
+}
+
+int enroll_ipcp(pid_t pid,
+ const char * dst,
+ const struct poa_addr * addr)
{
struct layer_info layer;
struct ipcp_info info;
@@ -402,7 +435,7 @@ int enroll_ipcp(pid_t pid,
goto fail;
}
- if (ipcp_enroll(pid, dst, &layer) < 0) {
+ if (ipcp_enroll(pid, dst, addr, &layer) < 0) {
log_err("Could not enroll IPCP %d.", pid);
goto fail;
}
@@ -421,10 +454,11 @@ int enroll_ipcp(pid_t pid,
return -1;
}
-int connect_ipcp(pid_t pid,
- const char * dst,
- const char * component,
- qosspec_t qs)
+int connect_ipcp(pid_t pid,
+ const char * dst,
+ const char * component,
+ qosspec_t qs,
+ const struct poa_addr * addr)
{
struct ipcp_info info;
@@ -442,7 +476,7 @@ int connect_ipcp(pid_t pid,
log_dbg("Connecting %s to %s.", component, dst);
- if (ipcp_connect(pid, dst, component, qs)) {
+ if (ipcp_connect(pid, dst, component, qs, addr) < 0) {
log_err("Could not connect IPCP %d to %s.", pid, dst);
return -EPERM;
}
@@ -453,6 +487,172 @@ int connect_ipcp(pid_t pid,
return 0;
}
+/* Resolve the dial name, if any, and fill in one record. */
+static int poa_addr_resolve(struct poa_addr * addr,
+ struct addrinfo ** res)
+{
+ struct addrinfo hints;
+
+ *res = NULL;
+
+ if (addr == NULL)
+ return 0;
+
+ if (addr->hostname[0] == '\0')
+ return addr->type == POA_UDP ? -EINVAL : 0;
+
+ if (addr->type != POA_UDP)
+ return -EINVAL;
+
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ if (getaddrinfo(addr->hostname, NULL, &hints, res) != 0) {
+ log_err("Failed to resolve %s.", addr->hostname);
+ return -EADDRNOTAVAIL;
+ }
+
+ return 0;
+}
+
+static void poa_addr_from_ai(struct poa_addr * addr,
+ const struct addrinfo * ai,
+ uint16_t port)
+{
+ struct sockaddr_in * in;
+ struct sockaddr_in6 * in6;
+
+ if (ai->ai_family == AF_INET) {
+ in = (struct sockaddr_in *) ai->ai_addr;
+ addr->type = POA_UDP4;
+ addr->udp4.ip_addr = in->sin_addr;
+ addr->udp4.port = port;
+ } else {
+ in6 = (struct sockaddr_in6 *) ai->ai_addr;
+ addr->type = POA_UDP6;
+ addr->udp6.ip_addr = in6->sin6_addr;
+ addr->udp6.port = port;
+ }
+}
+
+/* Skip families without an attached PoA; keep all if none reported. */
+static void poa_families(pid_t pid,
+ bool * v4,
+ bool * v6)
+{
+ struct poa_spec * eps;
+ ssize_t n;
+ ssize_t i;
+
+ eps = NULL;
+
+ *v4 = false;
+ *v6 = false;
+
+ n = list_poas(pid, &eps);
+ for (i = 0; i < n; i++) {
+ if (eps[i].type == POA_UDP4)
+ *v4 = true;
+
+ if (eps[i].type == POA_UDP6)
+ *v6 = true;
+ }
+
+ free(eps);
+
+ if (!*v4 && !*v6) {
+ *v4 = true;
+ *v6 = true;
+ }
+}
+
+/* Try each resolved record in order until an enrolment succeeds. */
+int enroll_ipcp_resolve(pid_t pid,
+ const char * dst,
+ struct poa_addr * addr)
+{
+ struct addrinfo * res;
+ struct addrinfo * ai;
+ uint16_t port;
+ bool v4;
+ bool v6;
+ int ret;
+
+ ret = poa_addr_resolve(addr, &res);
+ if (ret < 0)
+ return ret;
+
+ if (res == NULL)
+ return enroll_ipcp(pid, dst, addr);
+
+ port = addr->udp4.port; /* POA_UDP parks it there */
+
+ poa_families(pid, &v4, &v6);
+
+ ret = -EADDRNOTAVAIL;
+
+ for (ai = res; ai != NULL; ai = ai->ai_next) {
+ if ((ai->ai_family == AF_INET && !v4)
+ || (ai->ai_family == AF_INET6 && !v6))
+ continue;
+
+ poa_addr_from_ai(addr, ai, port);
+
+ ret = enroll_ipcp(pid, dst, addr);
+ if (ret == 0)
+ break;
+ }
+
+ freeaddrinfo(res);
+
+ return ret;
+}
+
+/* Try each resolved record in order until a connect succeeds. */
+int connect_ipcp_resolve(pid_t pid,
+ const char * dst,
+ const char * component,
+ qosspec_t qs,
+ struct poa_addr * addr)
+{
+ struct addrinfo * res;
+ struct addrinfo * ai;
+ uint16_t port;
+ bool v4;
+ bool v6;
+ int ret;
+
+ ret = poa_addr_resolve(addr, &res);
+ if (ret < 0)
+ return ret;
+
+ if (res == NULL)
+ return connect_ipcp(pid, dst, component, qs, addr);
+
+ port = addr->udp4.port; /* POA_UDP parks it there */
+
+ poa_families(pid, &v4, &v6);
+
+ ret = -EADDRNOTAVAIL;
+
+ for (ai = res; ai != NULL; ai = ai->ai_next) {
+ if ((ai->ai_family == AF_INET && !v4)
+ || (ai->ai_family == AF_INET6 && !v6))
+ continue;
+
+ poa_addr_from_ai(addr, ai, port);
+
+ ret = connect_ipcp(pid, dst, component, qs, addr);
+ if (ret == 0)
+ break;
+ }
+
+ freeaddrinfo(res);
+
+ return ret;
+}
+
static int disconnect_ipcp(pid_t pid,
const char * dst,
const char * component)
@@ -882,6 +1082,22 @@ static void __cleanup_flow(void * flow)
reg_destroy_flow(((struct flow_info *) flow)->id);
}
+/*
+ * A PoA flow is secured as the IPCP that owns it: trust is in the
+ * peer itself. Without a security config for that name the flow
+ * stays in the clear.
+ */
+static void poa_name_info(const char * name,
+ struct name_info * info)
+{
+ if (reg_get_name_info(name, info) == 0)
+ return;
+
+ memset(info, 0, sizeof(*info));
+
+ strcpy(info->name, name);
+}
+
static int flow_accept(struct flow_info * flow,
buffer_t * data,
struct timespec * abstime,
@@ -942,7 +1158,9 @@ static int flow_accept(struct flow_info * flow,
goto fail_oap;
}
- if (reg_get_name_info(name, &info) < 0) {
+ if (reg_flow_is_poa(flow->id)) {
+ poa_name_info(name, &info);
+ } else if (reg_get_name_info(name, &info) < 0) {
log_err("Failed to get name info for %s.", name);
err = -ENAME;
goto fail_oap;
@@ -1262,7 +1480,7 @@ static int flow_alloc_direct(const char * dst,
return -EAGAIN;
}
- if (oap_cli_prepare(&ctx, info, &req_hdr, *data, false) < 0) {
+ if (oap_cli_prepare(&ctx, info, NULL, &req_hdr, *data, false) < 0) {
log_err("Failed to prepare OAP for %s.", dst);
return -EBADF;
}
@@ -1295,7 +1513,7 @@ static int flow_alloc_direct(const char * dst,
return -ETIMEDOUT;
}
- err = oap_cli_complete(ctx, info, resp_hdr, data, sk, NULL, NULL);
+ err = oap_cli_complete(ctx, resp_hdr, data, sk, NULL, NULL);
if (err < 0) {
log_err("OAP completion failed for %s.", dst);
freebuf(resp_hdr);
@@ -1378,7 +1596,7 @@ static int flow_alloc(const char * dst,
goto fail_prepare;
}
- if (oap_cli_prepare(&ctx, &info, &req_hdr, *data, false) < 0) {
+ if (oap_cli_prepare(&ctx, &info, NULL, &req_hdr, *data, false) < 0) {
log_err("Failed to prepare OAP request for %s.", dst);
err = -EBADF;
goto fail_prepare;
@@ -1410,7 +1628,7 @@ static int flow_alloc(const char * dst,
goto fail_peer;
}
- err = oap_cli_complete(ctx, &info, resp_hdr, data, sk, NULL, &peer_crt);
+ err = oap_cli_complete(ctx, resp_hdr, data, sk, NULL, &peer_crt);
if (err < 0) {
log_err("OAP completion failed for %s.", dst);
goto fail_complete;
@@ -1444,6 +1662,199 @@ static int flow_alloc(const char * dst,
return err;
}
+/* Creates the flow and prepares the key exchange the IPCP will carry. */
+/* The N-1 is only known once the flow exists. */
+static int poa_alloc(struct flow_info * flow,
+ const char * dst,
+ buffer_t * data)
+{
+ struct ipcp_info ipcp;
+ struct layer_info layer;
+ struct name_info info;
+ buffer_t req_hdr = BUF_INIT;
+ void * ctx;
+ int err;
+
+ ipcp.pid = flow->n_pid;
+ if (reg_get_ipcp(&ipcp, &layer) < 0) {
+ log_err("No IPCP with pid %d.", flow->n_pid);
+
+ err = -EIPCP;
+ goto fail_flow;
+ }
+
+ flow->n_1_pid = 0;
+ if (reg_create_flow(flow) < 0) {
+ log_err("Failed to create PoA flow.");
+
+ err = -EBADF;
+ goto fail_flow;
+ }
+
+ reg_set_name_for_flow_id(ipcp.name, flow->id);
+
+ if (reg_flow_set_poa(flow->id) < 0) {
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ flow->uid = reg_get_proc_uid(flow->n_pid);
+ flow->n_1_pid = flow->n_pid; /* the IPCP is its own N-1 */
+ if (reg_prepare_flow_alloc(flow) < 0) {
+ log_err("Failed to prepare PoA flow allocation.");
+
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ poa_name_info(ipcp.name, &info);
+
+ if (oap_cli_prepare(&ctx, &info, dst, &req_hdr, *data, false) < 0) {
+ log_err("Failed to prepare OAP request for %s.", ipcp.name);
+
+ err = -EBADF;
+ goto fail_prepare;
+ }
+
+ if (reg_flow_set_oap_ctx(flow->id, ctx) < 0) {
+ err = -EBADF;
+ goto fail_ctx;
+ }
+
+ log_info("Allocating PoA flow %d for %d as %s.",
+ flow->id, flow->n_pid, ipcp.name);
+
+ *data = req_hdr;
+
+ return 0;
+
+ fail_ctx:
+ freebuf(req_hdr);
+ oap_ctx_free(ctx);
+ fail_prepare:
+ reg_destroy_flow(flow->id);
+ fail_flow:
+ return err;
+}
+
+/* Completes the key exchange once the IPCP has the peer's response. */
+/*
+ * We present the PoA's own credentials and expect the peer to present
+ * dst's: the address only says how to reach it. oap_cli_complete
+ * frees the ctx on every path.
+ */
+static int poa_complete(struct flow_info * flow,
+ int response,
+ buffer_t * data,
+ struct crypt_sk * sk)
+{
+ struct name_info info;
+ buffer_t peer_crt = BUF_INIT;
+ buffer_t out = BUF_INIT;
+ buffer_t empty = BUF_INIT;
+ char name[NAME_SIZE + 1];
+ void * ctx;
+ int err;
+
+ ctx = reg_flow_take_oap_ctx(flow->id);
+ if (ctx == NULL) {
+ log_err("No pending PoA flow %d.", flow->id);
+ return -EBADF;
+ }
+
+ if (response < 0) {
+ log_dbg("PoA flow %d refused: %d.", flow->id, response);
+
+ err = response;
+ goto fail;
+ }
+
+ if (reg_get_name_for_flow_id(name, flow->id) < 0) {
+ err = -EBADF;
+ goto fail;
+ }
+
+ poa_name_info(name, &info);
+
+ err = oap_cli_complete(ctx, *data, &out, sk, NULL, &peer_crt);
+ if (err < 0) {
+ log_err("OAP completion failed for %s.", name);
+
+ ctx = NULL;
+ goto fail;
+ }
+
+ if (sk->nid != NID_undef)
+ reg_flow_set_rekey(flow->id, true, peer_crt);
+
+ flow->state = FLOW_ALLOCATED;
+ if (reg_respond_alloc(flow, &empty, 0) < 0) {
+ log_err("Failed to update PoA flow %d.", flow->id);
+
+ err = -EBADF;
+ goto fail_crt;
+ }
+
+ log_info("PoA flow %d allocated to %s.", flow->id, name);
+
+ freebuf(peer_crt);
+ freebuf(out);
+
+ return 0;
+
+ fail_crt:
+ freebuf(peer_crt);
+ freebuf(out);
+ fail:
+ oap_ctx_free(ctx);
+ reg_destroy_flow(flow->id);
+ return err;
+}
+
+/* A peer requested a flow on a PoA of this IPCP. */
+static int poa_req_arr(struct flow_info * flow,
+ buffer_t * data)
+{
+ struct ipcp_info ipcp;
+ struct layer_info layer;
+ int ret;
+
+ ipcp.pid = flow->n_1_pid;
+ if (reg_get_ipcp(&ipcp, &layer) < 0) {
+ log_err("No IPCP with pid %d.", flow->n_1_pid);
+
+ ret = -EIPCP;
+ goto fail;
+ }
+
+ log_info("PoA flow request arrived for %s.", ipcp.name);
+
+ ret = wait_for_accept(ipcp.name);
+ if (ret < 0) {
+ log_err("No active process for %s.", ipcp.name);
+ goto fail;
+ }
+
+ flow->id = ret;
+ flow->state = FLOW_ALLOCATED;
+ if (reg_flow_set_poa(flow->id) < 0) {
+ ret = -EBADF;
+ goto fail;
+ }
+
+ reg_set_name_for_flow_id(ipcp.name, flow->id);
+
+ ret = reg_respond_accept(flow, data);
+ if (ret < 0) {
+ log_err("Failed to respond to PoA flow %d.", flow->id);
+ goto fail;
+ }
+
+ return 0;
+ fail:
+ return ret;
+}
+
static int flow_alloc_reply(struct flow_info * flow,
int response,
buffer_t * data)
@@ -1596,6 +2007,11 @@ static int rekey_name_info(int flow_id,
if (reg_get_name_for_flow_id(name, flow_id) < 0)
return -1;
+ if (reg_flow_is_poa(flow_id)) {
+ poa_name_info(name, info);
+ return 0;
+ }
+
return reg_get_name_info(name, info);
}
@@ -1667,7 +2083,7 @@ static void rekey_do_initiate(struct list_head * tbl,
goto fail;
}
- if (oap_cli_prepare(&ctx, &name, &req, data, true) < 0) {
+ if (oap_cli_prepare(&ctx, &name, NULL, &req, data, true) < 0) {
log_err("Failed to prepare re-key for flow %d.", flow_id);
goto fail;
}
@@ -1758,7 +2174,7 @@ static void rekey_do_complete(struct list_head * tbl,
reg_flow_get_peer_crt(flow_id, &crt);
/* oap_cli_complete frees the ctx on every path. */
- if (oap_cli_complete(e->ctx, &info, buf, &data, &sk, &crt, NULL) < 0) {
+ if (oap_cli_complete(e->ctx, buf, &data, &sk, &crt, NULL) < 0) {
log_warn("Failed to complete re-key for flow %d.", flow_id);
e->ctx = NULL;
goto finish_clear;
@@ -1933,7 +2349,7 @@ static void rekey_do_direct(int flow_id)
return;
}
- if (oap_cli_prepare(&ctx, &info, &req, data, true) < 0) {
+ if (oap_cli_prepare(&ctx, &info, NULL, &req, data, true) < 0) {
log_err("Failed to prepare re-key for flow %d.", flow_id);
reg_flow_clear_in_flight(flow_id);
return;
@@ -2307,6 +2723,10 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg,
pid_t cpid;
irm_msg_t * ret_msg;
buffer_t data;
+ struct poa_addr poa_addr;
+ struct poa_addr * pa;
+ struct poa_spec poa_spec;
+ struct poa_spec * eps = NULL;
memset(&flow, 0, sizeof(flow));
@@ -2350,11 +2770,74 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg,
res = bootstrap_ipcp(msg->pid, &conf);
break;
case IRM_MSG_CODE__IRM_ENROLL_IPCP:
- res = enroll_ipcp(msg->pid, msg->dst);
+ if (msg->peer != NULL) {
+ poa_addr = poa_addr_msg_to_s(msg->peer);
+ if (poa_addr.type == POA_INVALID) {
+ res = -EINVAL;
+ break;
+ }
+ }
+
+ if (msg->conf != NULL)
+ conf = ipcp_config_msg_to_s(msg->conf);
+ res = enroll_ipcp_resolve(msg->pid, msg->dst,
+ msg->peer != NULL ? &poa_addr : NULL);
+ break;
+ case IRM_MSG_CODE__IRM_ATTACH_IPCP:
+ poa_spec = poa_spec_msg_to_s(msg->poa);
+ if (poa_spec.type == POA_INVALID) {
+ res = -EINVAL;
+ break;
+ }
+
+ res = attach_ipcp(msg->pid, &poa_spec, true);
+ break;
+ case IRM_MSG_CODE__IRM_DETACH_IPCP:
+ poa_spec = poa_spec_msg_to_s(msg->poa);
+ if (poa_spec.type == POA_INVALID) {
+ res = -EINVAL;
+ break;
+ }
+
+ res = attach_ipcp(msg->pid, &poa_spec, false);
+ break;
+ case IRM_MSG_CODE__IRM_LIST_POAS:
+ res = list_poas(msg->pid, &eps);
+ if (res > 0) {
+ ssize_t i;
+ ret_msg->poas = malloc(res * sizeof(*ret_msg->poas));
+ if (ret_msg->poas == NULL) {
+ free(eps);
+
+ res = -ENOMEM;
+ break;
+ }
+ for (i = 0; i < res; i++) {
+ ret_msg->poas[i] = poa_spec_s_to_msg(&eps[i]);
+ if (ret_msg->poas[i] == NULL)
+ break;
+ ret_msg->n_poas++;
+ }
+ res = i == res ? 0 : -ENOMEM;
+ }
+ free(eps);
break;
case IRM_MSG_CODE__IRM_CONNECT_IPCP:
flow.qs = qos_spec_msg_to_s(msg->qosspec);
- res = connect_ipcp(msg->pid, msg->dst, msg->comp, flow.qs);
+
+ pa = NULL;
+
+ if (msg->peer != NULL) {
+ poa_addr = poa_addr_msg_to_s(msg->peer);
+ if (poa_addr.type == POA_INVALID) {
+ res = -EINVAL;
+ break;
+ }
+ pa = &poa_addr;
+ }
+
+ res = connect_ipcp_resolve(msg->pid, msg->dst, msg->comp,
+ flow.qs, pa);
break;
case IRM_MSG_CODE__IRM_DISCONNECT_IPCP:
res = disconnect_ipcp(msg->pid, msg->dst, msg->comp);
@@ -2499,6 +2982,67 @@ static irm_msg_t * do_command_msg(irm_msg_t * msg,
if (res == 0)
ret_msg->flow_info = flow_info_s_to_msg(&flow);
break;
+ case IRM_MSG_CODE__IRM_POA_FLOW_ALLOC:
+ flow = flow_info_msg_to_s(msg->flow_info);
+
+ clrbuf(data);
+
+ res = poa_alloc(&flow, msg->dst, &data);
+ if (res == 0) {
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_pk = true;
+ ret_msg->pk.len = data.len;
+ ret_msg->pk.data = data.data;
+
+ clrbuf(data);
+ }
+ break;
+ case IRM_MSG_CODE__IRM_POA_FLOW_ALLOC_R:
+ data.len = msg->pk.len;
+ data.data = msg->pk.data;
+ msg->pk.data = NULL; /* pass data */
+ msg->pk.len = 0;
+ flow = flow_info_msg_to_s(msg->flow_info);
+ sk.key = kbuf;
+ res = poa_complete(&flow, msg->response, &data, &sk);
+
+ freebuf(data);
+
+ if (res != 0)
+ break;
+
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ ret_msg->has_cipher_nid = true;
+ ret_msg->cipher_nid = sk.nid;
+
+ if (sk.nid == NID_undef)
+ break;
+
+ hbuf = malloc(SYMMKEYSZ);
+ if (hbuf == NULL) {
+ log_err("Failed to malloc key buf");
+
+ res = -ENOMEM;
+ break;
+ }
+
+ memcpy(hbuf, kbuf, SYMMKEYSZ);
+
+ ret_msg->sym_key.data = hbuf;
+ ret_msg->sym_key.len = SYMMKEYSZ;
+ ret_msg->has_sym_key = true;
+ break;
+ case IRM_MSG_CODE__IPCP_POA_FLOW_REQ_ARR:
+ data.len = msg->pk.len;
+ data.data = msg->pk.data;
+ msg->pk.data = NULL; /* pass data */
+ msg->pk.len = 0;
+ flow = flow_info_msg_to_s(msg->flow_info);
+
+ res = poa_req_arr(&flow, &data);
+ if (res == 0)
+ ret_msg->flow_info = flow_info_s_to_msg(&flow);
+ break;
case IRM_MSG_CODE__IPCP_FLOW_ALLOC_REPLY:
data.len = msg->pk.len;
data.data = msg->pk.data;