From eab20964b2f059898fc804584e55a157c021afc0 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Fri, 21 Jan 2011 18:00:36 +0100 Subject: udp: Make the SNMP code asynchronous Do not block the application when doing a SNMP request. Work with the results coming back from the callback. Right now a link can only be taken down and up. --- include/bsc_data.h | 2 +- include/snmp_mtp.h | 24 +++++++++++++++++++ src/link_udp.c | 60 +++++++++++++++++++++++++++++++++++++++--------- src/links.c | 7 +----- src/snmp_mtp.c | 67 +++++++++++++++++++++++++++++++++++++++++++++--------- 5 files changed, 131 insertions(+), 29 deletions(-) diff --git a/include/bsc_data.h b/include/bsc_data.h index 3311d53..1092d6e 100644 --- a/include/bsc_data.h +++ b/include/bsc_data.h @@ -141,7 +141,7 @@ void update_con_state(struct mtp_link_set *link, int rc, struct sccp_parse_resul unsigned int sls_for_src_ref(struct sccp_source_reference *ref); /* udp init */ -int link_global_init(struct mtp_udp_data *data, int src_port); +int link_global_init(struct mtp_udp_data *data, char *dest_ip, int src_port); int link_udp_init(struct mtp_udp_link *data, const char *dest_ip, int port); int link_init(struct bsc_data *bsc); int link_shutdown_all(struct mtp_link_set *); diff --git a/include/snmp_mtp.h b/include/snmp_mtp.h index 045e90f..8c15df7 100644 --- a/include/snmp_mtp.h +++ b/include/snmp_mtp.h @@ -26,6 +26,27 @@ struct snmp_mtp_session { netsnmp_session session, *ss; + void *data; + + /* + * The callbacks will be called multiple times. Even if + * we only toggle one object. Remember which request we + * are handling here and then we will claim success on the + * first of a series of PDUs. This is the easies to manage + * and if a link fails to come up the SLTM will catch it. + */ + int last_up_req; + int last_do_req; +}; + +enum { + SNMP_LINK_UP, + SNMP_LINK_DOWN, +}; + +enum { + SNMP_STATUS_OK, + SNMP_STATUS_TIMEOUT, }; struct snmp_mtp_session *snmp_mtp_session_create(char *host); @@ -33,4 +54,7 @@ void snmp_mtp_deactivate(struct snmp_mtp_session *, int link_id); void snmp_mtp_activate(struct snmp_mtp_session *, int link_id); void snmp_mtp_poll(); +/* to be implemented by the handler */ +void snmp_mtp_callback(struct snmp_mtp_session *, int area, int res, int link_id); + #endif diff --git a/src/link_udp.c b/src/link_udp.c index fd6b1f9..f7c6703 100644 --- a/src/link_udp.c +++ b/src/link_udp.c @@ -157,7 +157,6 @@ static void do_start(void *_data) struct mtp_udp_link *link = (struct mtp_udp_link *) _data; snmp_mtp_activate(link->data->session, link->link_index); - mtp_link_up(&link->base); } static int udp_link_reset(struct mtp_link *link) @@ -166,16 +165,7 @@ static int udp_link_reset(struct mtp_link *link) ulnk = (struct mtp_udp_link *) link; - LOGP(DINP, LOGL_NOTICE, "Will restart SLTM transmission in %d seconds.\n", - ulnk->reset_timeout); - snmp_mtp_deactivate(ulnk->data->session, ulnk->link_index); - mtp_link_down(link); - - /* restart the link in 90 seconds... to force a timeout on the BSC */ - link->link_activate.cb = do_start; - link->link_activate.data = link; - bsc_schedule_timer(&link->link_activate, ulnk->reset_timeout, 0); return 0; } @@ -239,12 +229,18 @@ static void snmp_poll(void *_data) bsc_schedule_timer(&data->snmp_poll, 0, 5000); } -int link_global_init(struct mtp_udp_data *data, int src_port) +int link_global_init(struct mtp_udp_data *data, char *udp_ip, int src_port) { struct sockaddr_in addr; int fd; int on; + /* setup SNMP first, it is blocking */ + data->session = snmp_mtp_session_create(udp_ip); + if (!data->session) + return -1; + data->session->data = data; + INIT_LLIST_HEAD(&data->links); write_queue_init(&data->write_queue, 100); @@ -287,3 +283,45 @@ int link_global_init(struct mtp_udp_data *data, int src_port) return 0; } + +void snmp_mtp_callback(struct snmp_mtp_session *session, + int area, int res, int link_id) +{ + struct mtp_udp_data *data; + struct mtp_udp_link *ulink; + struct mtp_link *link; + + data = session->data; + ulink = find_link(data, link_id); + if (!ulink) + return LOGP(DINP, LOGL_ERROR, "Failed to find link %d\n", link_id); + + link = &ulink->base; + + if (res == SNMP_STATUS_TIMEOUT) { + LOGP(DINP, LOGL_ERROR, "Failed to restart link: %d\n", link_id); + udp_link_reset(link); + return; + } + + switch (area) { + case SNMP_LINK_UP: + mtp_link_up(link); + break; + case SNMP_LINK_DOWN: + mtp_link_down(link); + + /* + * restart the link in 90 seconds... + * to force a timeout on the BSC + */ + link->link_activate.cb = do_start; + link->link_activate.data = link; + bsc_schedule_timer(&link->link_activate, ulink->reset_timeout, 0); + LOGP(DINP, LOGL_NOTICE, + "Will restart SLTM transmission in %d seconds.\n", ulink->reset_timeout); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unknown event %d\n", area); + } +} diff --git a/src/links.c b/src/links.c index bf282b6..4342447 100644 --- a/src/links.c +++ b/src/links.c @@ -126,12 +126,7 @@ int link_init(struct bsc_data *bsc) LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n"); - /* setup SNMP first, it is blocking */ - bsc->udp_data.session = snmp_mtp_session_create(bsc->udp_ip); - if (!bsc->udp_data.session) - return -1; - - if (link_global_init(&bsc->udp_data, bsc->src_port) != 0) + if (link_global_init(&bsc->udp_data, bsc->udp_ip, bsc->src_port) != 0) return -1; /* now connect to the transport */ diff --git a/src/snmp_mtp.c b/src/snmp_mtp.c index ec4d892..2cdf323 100644 --- a/src/snmp_mtp.c +++ b/src/snmp_mtp.c @@ -41,39 +41,83 @@ static void add_pdu_var(netsnmp_pdu *pdu, const char *mib_name, int id, const ch } } -static void send_pdu(netsnmp_session *ss, netsnmp_pdu *pdu) +static int link_up_cb(int op, netsnmp_session *ss, + int reqid, netsnmp_pdu *pdu, void *data) +{ + struct snmp_mtp_session *mtp = ss->myvoid; + int link_index = (int) data; + + if (mtp->last_up_req != reqid) + return 0; + + mtp->last_up_req = 0; + snmp_mtp_callback(mtp, SNMP_LINK_UP, + op == STAT_TIMEOUT ? SNMP_STATUS_TIMEOUT : SNMP_STATUS_OK, + link_index); + return 0; +} + +static int link_down_cb(int op, netsnmp_session *ss, + int reqid, netsnmp_pdu *pdu, void *data) +{ + struct snmp_mtp_session *mtp = ss->myvoid; + int link_index = (int) data; + + if (mtp->last_do_req != reqid) + return 0; + + mtp->last_do_req = 0; + snmp_mtp_callback(mtp, SNMP_LINK_DOWN, + op == STAT_TIMEOUT ? SNMP_STATUS_TIMEOUT : SNMP_STATUS_OK, + link_index); + return 0; +} + +static int send_pdu(netsnmp_session *ss, netsnmp_pdu *pdu, int kind, int link_id) { int status; - netsnmp_pdu *response; + netsnmp_callback cb; + + cb = kind == SNMP_LINK_UP ? link_up_cb : link_down_cb; - status = snmp_synch_response(ss, pdu, &response); - if (status == STAT_ERROR) { - snmp_sess_perror("set failed", ss); - } else if (status == STAT_TIMEOUT) { - fprintf(stderr, "Timeout for SNMP.\n"); + status = snmp_async_send(ss, pdu, cb, (void *) link_id); + if (status == 0) { + snmp_log(LOG_ERR, "Failed to send async request.\n"); + snmp_free_pdu(pdu); } - if (response) - snmp_free_pdu(response); + return status; } static void snmp_mtp_start_c7_datalink(struct snmp_mtp_session *session, int link_id) { + int status; netsnmp_pdu *pdu; pdu = snmp_pdu_create(SNMP_MSG_SET); add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7DatalinkCommand", link_id, "nwc7DatalinkCmdPowerOn"); add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7Mtp2Active", link_id, "true"); - send_pdu(session->ss, pdu); + status = send_pdu(session->ss, pdu, SNMP_LINK_UP, link_id); + + if (status == 0) + snmp_mtp_callback(session, SNMP_LINK_UP, SNMP_STATUS_TIMEOUT, link_id); + else + session->last_up_req = status; + } static void snmp_mtp_stop_c7_datalink(struct snmp_mtp_session *session, int link_id) { + int status; netsnmp_pdu *pdu; pdu = snmp_pdu_create(SNMP_MSG_SET); add_pdu_var(pdu, "PTI-NexusWareC7-MIB::nwc7Mtp2Active", link_id, "false"); - send_pdu(session->ss, pdu); + status = send_pdu(session->ss, pdu, SNMP_LINK_DOWN, link_id); + if (status == 0) + snmp_mtp_callback(session, SNMP_LINK_DOWN, SNMP_STATUS_TIMEOUT, link_id); + else + session->last_do_req = status; } struct snmp_mtp_session *snmp_mtp_session_create(char *host) @@ -88,6 +132,7 @@ struct snmp_mtp_session *snmp_mtp_session_create(char *host) session->session.version = SNMP_VERSION_1; session->session.community = (unsigned char *) "private"; session->session.community_len = strlen((const char *) session->session.community); + session->session.myvoid = session; session->ss = snmp_open(&session->session); if (!session->ss) { -- cgit v1.2.3