diff --git a/src/osmo-pfcp-tool/pfcp_tool_vty.c b/src/osmo-pfcp-tool/pfcp_tool_vty.c index 35f7448..1bb6b1f 100644 --- a/src/osmo-pfcp-tool/pfcp_tool_vty.c +++ b/src/osmo-pfcp-tool/pfcp_tool_vty.c @@ -402,6 +402,9 @@ enum pdr_id_fixed { PDR_ID_ACCESS = 2, }; +const char * const gtp_ip_core = "10.99.0.1"; +const char * const gtp_ip_access = "10.99.0.2"; + int session_tunend_tx_est_req(struct pfcp_tool_session *session, bool forw, osmo_pfcp_resp_cb resp_cb) { struct pfcp_tool_peer *peer = session->peer; @@ -818,6 +821,294 @@ DEFUN(session_tx_del_req, session_tx_del_req_cmd, return CMD_SUCCESS; } +/* N SESSIONS */ + +static int responses_pending = 0; + +DEFUN(wait_responses, wait_responses_cmd, + "wait responses", + "Let some time pass until events have occured\n" + "Wait for all PFCP responses for pending PFCP requests\n") +{ + if (!responses_pending) { + vty_out(vty, "no responses pending, not waiting.%s", VTY_NEWLINE); + return CMD_SUCCESS; + } + + vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE); + vty_flush(vty); + + /* Still operate the message pump while waiting for time to pass */ + while (!osmo_select_shutdown_done()) { + if (pfcp_tool_mainloop(0) == -1) + break; + if (responses_pending == 0) + break; + } + + vty_out(vty, "...done waiting for responses%s", VTY_NEWLINE); + vty_flush(vty); + return CMD_SUCCESS; +} + +/* N SESSIONS TUNEND */ + +int one_session_mod_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg) +{ + responses_pending--; + return 0; +} + +int one_session_mod_tunend(struct pfcp_tool_session *session) +{ + int rc; + + rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunend_resp_cb); + if (rc == CMD_SUCCESS) + responses_pending++; + return rc; +} + +void est_resp_get_created_f_teid(struct pfcp_tool_gtp_tun_ep *dst, const struct osmo_pfcp_msg *rx_resp, uint16_t pdr_id) +{ + int i; + const struct osmo_pfcp_msg_session_est_resp *r; + if (rx_resp->h.message_type != OSMO_PFCP_MSGT_SESSION_EST_RESP) + return; + + r = &rx_resp->ies.session_est_resp; + + for (i = 0; i < r->created_pdr_count; i++) { + const struct osmo_pfcp_ie_created_pdr *p = &r->created_pdr[i]; + if (p->pdr_id != pdr_id) + continue; + if (!p->local_f_teid_present) + continue; + osmo_sockaddr_str_from_sockaddr(&dst->addr, + &p->local_f_teid.fixed.ip_addr.v4.u.sas); + dst->teid = p->local_f_teid.fixed.teid; + } +} + +int one_session_create_tunend_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg) +{ + struct pfcp_tool_session *session = req->ctx.priv; + enum osmo_pfcp_cause *cause; + const struct osmo_pfcp_msg_session_est_resp *r = NULL; + if (rx_resp) + r = &rx_resp->ies.session_est_resp; + + responses_pending--; + + if (errmsg) + LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg); + + cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL; + if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) + return 0; + + /* store SEID */ + if (r && r->up_f_seid_present) + session->up_f_seid = r->up_f_seid; + /* store access local F-TEID */ + est_resp_get_created_f_teid(&session->tunend.access.local, rx_resp, PDR_ID_ACCESS); + + /* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */ + one_session_mod_tunend(session); + return 0; +} + +static int one_session_create_tunend(struct pfcp_tool_peer *peer) +{ + struct pfcp_tool_session *session; + struct pfcp_tool_tunend *te; + struct pfcp_tool_gtp_tun_ep *dst; + int rc; + + session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNEND); + te = &session->tunend; + + /* Access: set remote GTP address */ + dst = &te->access.remote; + if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_access)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n", + osmo_quote_cstr_c(OTC_SELECT, gtp_ip_access, -1)); + return CMD_WARNING; + } + dst->teid = pfcp_tool_new_teid(); + + /* Set local F-TEIDs == CHOOSE */ + te->access.local = (struct pfcp_tool_gtp_tun_ep){}; + pfcp_tool_next_ue_addr(&te->core.ue_local_addr); + + /* Send initial Session Establishment Request */ + rc = session_tunend_tx_est_req(session, false, one_session_create_tunend_resp_cb); + if (rc == CMD_SUCCESS) + responses_pending++; + return rc; +} + +/* N SESSIONS TUNMAP */ + +int one_session_mod_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg) +{ + responses_pending--; + return 0; +} + +int one_session_mod_tunmap(struct pfcp_tool_session *session) +{ + struct pfcp_tool_gtp_tun_ep *dst; + int rc; + + dst = &session->tunmap.core.remote; + if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_core)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n", + osmo_quote_cstr_c(OTC_SELECT, gtp_ip_core, -1)); + return CMD_WARNING; + } + dst->teid = pfcp_tool_new_teid(); + + rc = session_tunmap_tx_mod_req(session, true, one_session_mod_tunmap_resp_cb); + if (rc == CMD_SUCCESS) + responses_pending++; + return rc; +} + +int one_session_create_tunmap_resp_cb(struct osmo_pfcp_msg *req, struct osmo_pfcp_msg *rx_resp, const char *errmsg) +{ + struct pfcp_tool_session *session = req->ctx.priv; + enum osmo_pfcp_cause *cause; + + responses_pending--; + + if (errmsg) + LOGP(DLPFCP, LOGL_ERROR, "%s\n", errmsg); + + cause = rx_resp ? osmo_pfcp_msg_cause(rx_resp) : NULL; + if (!cause || *cause != OSMO_PFCP_CAUSE_REQUEST_ACCEPTED) + return 0; + + /* store SEID */ + if (rx_resp->ies.session_est_resp.up_f_seid_present) + session->up_f_seid = rx_resp->ies.session_est_resp.up_f_seid; + + /* store local F-TEIDs */ + est_resp_get_created_f_teid(&session->tunmap.access.local, rx_resp, PDR_ID_ACCESS); + est_resp_get_created_f_teid(&session->tunmap.core.local, rx_resp, PDR_ID_CORE); + + /* Success response, now continue with second step: Session Mod to set the CORE's remote side GTP */ + one_session_mod_tunmap(session); + return 0; +} + +static int one_session_create_tunmap(struct pfcp_tool_peer *peer) +{ + struct pfcp_tool_session *session; + struct pfcp_tool_tunmap *tm; + struct pfcp_tool_gtp_tun_ep *dst; + int rc; + + session = pfcp_tool_session_find_or_create(peer, peer_new_seid(peer), UP_GTP_U_TUNMAP); + tm = &session->tunmap; + + /* Access: set remote GTP address */ + dst = &tm->access.remote; + if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_access)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n", + osmo_quote_cstr_c(OTC_SELECT, gtp_ip_access, -1)); + return CMD_WARNING; + } + dst->teid = pfcp_tool_new_teid(); + + /* Core: set remote GTP address */ + dst = &tm->core.remote; + if (osmo_sockaddr_str_from_str2(&dst->addr, gtp_ip_core)) { + LOGP(DLGLOBAL, LOGL_ERROR, "Error setting GTP IP address from %s\n", + osmo_quote_cstr_c(OTC_SELECT, gtp_ip_core, -1)); + return CMD_WARNING; + } + dst->teid = pfcp_tool_new_teid(); + + /* Set local F-TEIDs == CHOOSE */ + tm->access.local = (struct pfcp_tool_gtp_tun_ep){}; + tm->core.local = (struct pfcp_tool_gtp_tun_ep){}; + + /* Send initial Session Establishment Request */ + rc = session_tunmap_tx_est_req(session, false, one_session_create_tunmap_resp_cb); + if (rc == CMD_SUCCESS) + responses_pending++; + return rc; + return CMD_WARNING; +} + +static void n_sessions_create(struct vty *vty, struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind) +{ + int i; + for (i = 0; i < n; i++) { + int rc; + if (kind == UP_GTP_U_TUNMAP) + rc = one_session_create_tunmap(peer); + else + rc = one_session_create_tunend(peer); + if (rc != CMD_SUCCESS) + break; + + /* handle any pending select work */ + while (!osmo_select_shutdown_done()) { + rc = pfcp_tool_mainloop(1); + /* quit requested */ + if (rc < 0) + return; + /* no fd needed service */ + if (rc == 0) + break; + } + + /* Every N created sessions, wait for pending responses */ + if (!(i & 0x3f) && responses_pending) { + vty_out(vty, "waiting for %d responses...%s", responses_pending, VTY_NEWLINE); + vty_flush(vty); + while (!osmo_select_shutdown_done()) { + if (pfcp_tool_mainloop(0) == -1) + break; + if (responses_pending == 0) + break; + } + } + } +} + +static void n_sessions_delete(struct pfcp_tool_peer *peer, int n, enum up_gtp_action_kind kind) +{ +} + +DEFUN(n_sessions, n_sessions_cmd, + "n (<0-2147483647>|all) session (create|delete) (tunend|tunmap)", + "Batch run\n" + "Perform the action N times, or on all available entries\n" + "In a batch run, create and later delete a number of sessions at once.\n" + "Create N new sessions\n" + "Delete N sessions created earlier\n" + TUNEND_STR TUNMAP_STR) +{ + struct pfcp_tool_peer *peer = vty->index; + int n = (strcmp("all", argv[0]) == 0? -1 : atoi(argv[0])); + bool create = (strcmp("create", argv[1]) == 0); + enum up_gtp_action_kind kind; + if (!strcmp(argv[2], "tunmap")) + kind = UP_GTP_U_TUNMAP; + else + kind = UP_GTP_U_TUNEND; + + if (create) + n_sessions_create(vty, peer, n, kind); + else + n_sessions_delete(peer, n, kind); + return CMD_SUCCESS; +} + + static void install_ve_and_config(struct cmd_element *cmd) { install_element_ve(cmd); @@ -837,6 +1128,7 @@ void pfcp_tool_vty_init_cmds() OSMO_ASSERT(g_pfcp_tool != NULL); install_ve_and_config(&c_sleep_cmd); + install_ve_and_config(&wait_responses_cmd); install_ve_and_config(&peer_cmd); install_node(&peer_node, NULL); @@ -845,6 +1137,8 @@ void pfcp_tool_vty_init_cmds() install_element(PEER_NODE, &peer_tx_heartbeat_cmd); install_element(PEER_NODE, &peer_tx_assoc_setup_req_cmd); install_element(PEER_NODE, &peer_retrans_req_cmd); + install_element(PEER_NODE, &n_sessions_cmd); + install_element(PEER_NODE, &wait_responses_cmd); install_element(PEER_NODE, &session_cmd); install_element(PEER_NODE, &session_endecaps_cmd);