From 0e2f9116f846968d5eaf90cf21b7648f1964ae8b Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Mon, 17 Jan 2011 11:54:39 +0100 Subject: mtp: Rename link_data to mtp_link and move out the transport specific things Rename link_data to mtp_link and move it into the mtp_data header file, also remove the union to ease creating more of the subtypes. This is done in preparation to the linkset knowing more about the link (e.g. having a link test per link instead of per link). --- include/bsc_data.h | 49 ++++++++++----------------------- include/mtp_data.h | 33 ++++++++++++++++++---- src/link_udp.c | 80 ++++++++++++++++++++++++++++++------------------------ src/links.c | 42 ++++++++++++++-------------- src/mtp_layer3.c | 10 +++---- 5 files changed, 112 insertions(+), 102 deletions(-) diff --git a/include/bsc_data.h b/include/bsc_data.h index fa04c3a..932b229 100644 --- a/include/bsc_data.h +++ b/include/bsc_data.h @@ -22,6 +22,8 @@ #ifndef BSC_DATA_H #define BSC_DATA_H +#include "mtp_data.h" + #include #include #include @@ -38,40 +40,17 @@ struct bsc_data; struct snmp_mtp_session; -/** - * A link to the underlying MTP2 library or such - */ -struct link_data { - struct llist_head entry; - - union { - struct { - struct thread_notifier *notifier; - struct llist_head mtp_queue; - struct timer_list mtp_timeout; - } c7; - struct { - struct write_queue write_queue; - struct sockaddr_in remote; - struct snmp_mtp_session *session; - int link_index; - int reset_timeout; - } udp; - }; +struct mtp_udp_link { + /* subclass */ + struct mtp_link base; - int pcap_fd; + /* UDP specific stuff */ struct bsc_data *bsc; - struct mtp_link_set *the_link; - - int available; - - struct timer_list link_activate; - - int (*start)(struct link_data *); - int (*write)(struct link_data *, struct msgb *msg); - int (*shutdown)(struct link_data *); - int (*reset)(struct link_data *data); - int (*clear_queue)(struct link_data *data); + struct write_queue write_queue; + struct sockaddr_in remote; + struct snmp_mtp_session *session; + int link_index; + int reset_timeout; }; @@ -131,8 +110,8 @@ struct bsc_data { /* bsc related functions */ void release_bsc_resources(struct bsc_data *bsc); -void mtp_link_down(struct link_data *data); -void mtp_link_up(struct link_data *data); +void mtp_link_down(struct mtp_link *data); +void mtp_link_up(struct mtp_link *data); void mtp_linkset_down(struct mtp_link_set *); void mtp_linkset_up(struct mtp_link_set *); @@ -150,7 +129,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_udp_init(struct link_data *data, int src_port, const char *dest_ip, int port); +int link_udp_init(struct mtp_udp_link *data, int src_port, const char *dest_ip, int port); int link_init(struct bsc_data *bsc); int link_shutdown_all(struct mtp_link_set *); int link_reset_all(struct mtp_link_set *); diff --git a/include/mtp_data.h b/include/mtp_data.h index 349bf7a..fd886a1 100644 --- a/include/mtp_data.h +++ b/include/mtp_data.h @@ -25,7 +25,7 @@ #include struct bsc_data; -struct link_data; +struct mtp_link; /* MTP Level3 timers */ @@ -65,12 +65,35 @@ struct mtp_link_set { struct timer_list delay_timer; struct llist_head links; - struct link_data *slc[16]; + struct mtp_link *slc[16]; /* custom data */ struct bsc_data *bsc; }; +/** + * One physical link to somewhere. This is the base + * with the interface used by the mtp_link_set. There + * will be specific implementations for M2UA, UDP and + * other transport means. + */ +struct mtp_link { + struct llist_head entry; + + int pcap_fd; + struct mtp_link_set *the_link; + + int available; + + struct timer_list link_activate; + + int (*start)(struct mtp_link *); + int (*write)(struct mtp_link *, struct msgb *msg); + int (*shutdown)(struct mtp_link *); + int (*reset)(struct mtp_link *data); + int (*clear_queue)(struct mtp_link *data); +}; + struct mtp_link_set *mtp_link_set_alloc(void); void mtp_link_set_stop(struct mtp_link_set *link); @@ -80,16 +103,16 @@ int mtp_link_set_submit_sccp_data(struct mtp_link_set *link, int sls, const uint int mtp_link_set_submit_isup_data(struct mtp_link_set *link, int sls, const uint8_t *data, unsigned int length); void mtp_link_set_init_slc(struct mtp_link_set *set); -void mtp_link_set_add_link(struct mtp_link_set *set, struct link_data *link); +void mtp_link_set_add_link(struct mtp_link_set *set, struct mtp_link *link); /* one time init function */ void mtp_link_set_init(void); /* to be implemented for MSU sending */ -void mtp_link_set_submit(struct link_data *link, struct msgb *msg); +void mtp_link_set_submit(struct mtp_link *link, struct msgb *msg); void mtp_link_set_forward_sccp(struct mtp_link_set *link, struct msgb *msg, int sls); -void mtp_link_restart(struct link_data *link); +void mtp_link_restart(struct mtp_link *link); void mtp_link_set_sccp_down(struct mtp_link_set *link); #endif diff --git a/src/link_udp.c b/src/link_udp.c index e05bd17..09b680f 100644 --- a/src/link_udp.c +++ b/src/link_udp.c @@ -37,18 +37,18 @@ static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg) { - struct link_data *link; + struct mtp_udp_link *link; int rc; - link = (struct link_data *) fd->data; + link = fd->data; LOGP(DINP, LOGL_DEBUG, "Sending MSU: %s\n", hexdump(msg->data, msg->len)); - if (link->pcap_fd >= 0) - mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg)); + if (link->base.pcap_fd >= 0) + mtp_pcap_write_msu(link->base.pcap_fd, msg->l2h, msgb_l2len(msg)); /* the assumption is we have connected the socket to the remote */ rc = sendto(fd->fd, msg->data, msg->len, 0, - (struct sockaddr *) &link->udp.remote, sizeof(link->udp.remote)); + (struct sockaddr *) &link->remote, sizeof(link->remote)); if (rc != msg->len) { LOGP(DINP, LOGL_ERROR, "Failed to write msg to socket: %d\n", rc); return -1; @@ -59,7 +59,7 @@ static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg) static int udp_read_cb(struct bsc_fd *fd) { - struct link_data *link; + struct mtp_link *link; struct udp_data_hdr *hdr; struct msgb *msg; int rc; @@ -72,7 +72,7 @@ static int udp_read_cb(struct bsc_fd *fd) } - link = (struct link_data *) fd->data; + link = (struct mtp_link *) fd->data; rc = read(fd->fd, msg->data, 2096); if (rc < sizeof(*hdr)) { LOGP(DINP, LOGL_ERROR, "Failed to read at least size of the header: %d\n", rc); @@ -120,7 +120,7 @@ exit: return rc; } -static int udp_link_dummy(struct link_data *link) +static int udp_link_dummy(struct mtp_link *link) { /* nothing todo */ return 0; @@ -128,38 +128,46 @@ static int udp_link_dummy(struct link_data *link) static void do_start(void *_data) { - struct link_data *link = (struct link_data *) _data; + struct mtp_udp_link *link = (struct mtp_udp_link *) _data; - snmp_mtp_activate(link->udp.session, link->udp.link_index); - mtp_link_up(link); + snmp_mtp_activate(link->session, link->link_index); + mtp_link_up(&link->base); } -static int udp_link_reset(struct link_data *link) +static int udp_link_reset(struct mtp_link *link) { + struct mtp_udp_link *ulnk; + + ulnk = (struct mtp_udp_link *) link; + LOGP(DINP, LOGL_NOTICE, "Will restart SLTM transmission in %d seconds.\n", - link->udp.reset_timeout); - snmp_mtp_deactivate(link->udp.session, link->udp.link_index); + ulnk->reset_timeout); + + snmp_mtp_deactivate(ulnk->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, link->udp.reset_timeout, 0); + bsc_schedule_timer(&link->link_activate, ulnk->reset_timeout, 0); return 0; } -static int udp_link_write(struct link_data *link, struct msgb *msg) +static int udp_link_write(struct mtp_link *link, struct msgb *msg) { + struct mtp_udp_link *ulnk; struct udp_data_hdr *hdr; + ulnk = (struct mtp_udp_link *) link; + hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr)); hdr->format_type = UDP_FORMAT_SIMPLE_UDP; hdr->data_type = UDP_DATA_MSU_PRIO_0; - hdr->data_link_index = htons(link->udp.link_index); + hdr->data_link_index = htons(ulnk->link_index); hdr->user_context = 0; hdr->data_length = htonl(msgb_l2len(msg)); - if (write_queue_enqueue(&link->udp.write_queue, msg) != 0) { + if (write_queue_enqueue(&ulnk->write_queue, msg) != 0) { LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg.\n"); msgb_free(msg); return -1; @@ -168,36 +176,36 @@ static int udp_link_write(struct link_data *link, struct msgb *msg) return 0; } -static int udp_link_start(struct link_data *link) +static int udp_link_start(struct mtp_link *link) { LOGP(DINP, LOGL_NOTICE, "UDP input is ready.\n"); do_start(link); return 0; } -int link_udp_init(struct link_data *link, int src_port, const char *remote, int remote_port) +int link_udp_init(struct mtp_udp_link *link, int src_port, const char *remote, int remote_port) { struct sockaddr_in addr; int fd; int on; - write_queue_init(&link->udp.write_queue, 100); + write_queue_init(&link->write_queue, 100); /* function table */ - link->shutdown = udp_link_dummy; - link->clear_queue = udp_link_dummy; + link->base.shutdown = udp_link_dummy; + link->base.clear_queue = udp_link_dummy; - link->reset = udp_link_reset; - link->start = udp_link_start; - link->write = udp_link_write; + link->base.reset = udp_link_reset; + link->base.start = udp_link_start; + link->base.write = udp_link_write; /* socket creation */ - link->udp.write_queue.bfd.data = link; - link->udp.write_queue.bfd.when = BSC_FD_READ; - link->udp.write_queue.read_cb = udp_read_cb; - link->udp.write_queue.write_cb = udp_write_cb; + link->write_queue.bfd.data = link; + link->write_queue.bfd.when = BSC_FD_READ; + link->write_queue.read_cb = udp_read_cb; + link->write_queue.write_cb = udp_write_cb; - link->udp.write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0); + link->write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0); if (fd < 0) { LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n"); return -1; @@ -218,12 +226,12 @@ int link_udp_init(struct link_data *link, int src_port, const char *remote, int } /* now connect the socket to the remote */ - memset(&link->udp.remote, 0, sizeof(link->udp.remote)); - link->udp.remote.sin_family = AF_INET; - link->udp.remote.sin_port = htons(remote_port); - inet_aton(remote, &link->udp.remote.sin_addr); + memset(&link->remote, 0, sizeof(link->remote)); + link->remote.sin_family = AF_INET; + link->remote.sin_port = htons(remote_port); + inet_aton(remote, &link->remote.sin_addr); - if (bsc_register_fd(&link->udp.write_queue.bfd) != 0) { + if (bsc_register_fd(&link->write_queue.bfd) != 0) { LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n"); close(fd); return -1; diff --git a/src/links.c b/src/links.c index 023a7f4..1134e59 100644 --- a/src/links.c +++ b/src/links.c @@ -31,7 +31,7 @@ extern struct bsc_data bsc; int is_one_up(struct mtp_link_set *set) { - struct link_data *entry; + struct mtp_link *entry; llist_for_each_entry(entry, &set->links, entry) if (entry->available) @@ -39,7 +39,7 @@ int is_one_up(struct mtp_link_set *set) return 0; } -void mtp_link_down(struct link_data *link) +void mtp_link_down(struct mtp_link *link) { int one_up; int was_up; @@ -55,7 +55,7 @@ void mtp_link_down(struct link_data *link) mtp_link_set_init_slc(link->the_link); } -void mtp_link_up(struct link_data *link) +void mtp_link_up(struct mtp_link *link) { int one_up; @@ -71,12 +71,12 @@ void mtp_link_set_sccp_down(struct mtp_link_set *link) { } -void mtp_link_set_submit(struct link_data *link, struct msgb *msg) +void mtp_link_set_submit(struct mtp_link *link, struct msgb *msg) { link->write(link, msg); } -void mtp_link_restart(struct link_data *link) +void mtp_link_restart(struct mtp_link *link) { LOGP(DINP, LOGL_ERROR, "Need to restart the SS7 link.\n"); link->reset(link); @@ -84,7 +84,7 @@ void mtp_link_restart(struct link_data *link) static void start_rest(void *start) { - struct link_data *data; + struct mtp_link *data; bsc.setup = 1; if (msc_init(&bsc, 1) != 0) { @@ -98,7 +98,7 @@ static void start_rest(void *start) int link_init(struct bsc_data *bsc) { - struct link_data *lnk; + struct mtp_udp_link *lnk; bsc->link_set = mtp_link_set_alloc(); bsc->link_set->dpc = bsc->dpc; @@ -109,13 +109,13 @@ int link_init(struct bsc_data *bsc) bsc->link_set->spare = bsc->ni_spare; bsc->link_set->bsc = bsc; - lnk = talloc_zero(bsc->link_set, struct link_data); + lnk = talloc_zero(bsc->link_set, struct mtp_udp_link); + lnk->base.pcap_fd = bsc->pcap_fd; + lnk->base.the_link = bsc->link_set; lnk->bsc = bsc; - lnk->udp.link_index = 1; - lnk->pcap_fd = bsc->pcap_fd; - lnk->udp.reset_timeout = bsc->udp_reset_timeout; - lnk->the_link = bsc->link_set; - mtp_link_set_add_link(bsc->link_set, lnk); + lnk->link_index = 1; + lnk->reset_timeout = bsc->udp_reset_timeout; + mtp_link_set_add_link(bsc->link_set, (struct mtp_link *) lnk); if (!bsc->src_port) { LOGP(DINP, LOGL_ERROR, "You need to set a UDP address.\n"); @@ -125,8 +125,8 @@ int link_init(struct bsc_data *bsc) LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n"); /* setup SNMP first, it is blocking */ - lnk->udp.session = snmp_mtp_session_create(bsc->udp_ip); - if (!lnk->udp.session) + lnk->session = snmp_mtp_session_create(bsc->udp_ip); + if (!lnk->session) return -1; /* now connect to the transport */ @@ -139,11 +139,11 @@ int link_init(struct bsc_data *bsc) * SLTM and it begins a reset. Then we will take it up * again and do the usual business. */ - snmp_mtp_deactivate(lnk->udp.session, - lnk->udp.link_index); + snmp_mtp_deactivate(lnk->session, + lnk->link_index); bsc->start_timer.cb = start_rest; bsc->start_timer.data = &bsc; - bsc_schedule_timer(&bsc->start_timer, lnk->udp.reset_timeout, 0); + bsc_schedule_timer(&bsc->start_timer, lnk->reset_timeout, 0); LOGP(DMSC, LOGL_NOTICE, "Making sure SLTM will timeout.\n"); return 0; @@ -151,7 +151,7 @@ int link_init(struct bsc_data *bsc) int link_shutdown_all(struct mtp_link_set *set) { - struct link_data *lnk; + struct mtp_link *lnk; llist_for_each_entry(lnk, &set->links, entry) lnk->shutdown(lnk); @@ -160,7 +160,7 @@ int link_shutdown_all(struct mtp_link_set *set) int link_reset_all(struct mtp_link_set *set) { - struct link_data *lnk; + struct mtp_link *lnk; llist_for_each_entry(lnk, &set->links, entry) lnk->reset(lnk); @@ -169,7 +169,7 @@ int link_reset_all(struct mtp_link_set *set) int link_clear_all(struct mtp_link_set *set) { - struct link_data *lnk; + struct mtp_link *lnk; llist_for_each_entry(lnk, &set->links, entry) lnk->clear_queue(lnk); diff --git a/src/mtp_layer3.c b/src/mtp_layer3.c index 531e121..c3f3878 100644 --- a/src/mtp_layer3.c +++ b/src/mtp_layer3.c @@ -551,11 +551,11 @@ static int mtp_int_submit(struct mtp_link_set *link, int pc, int sls, int type, return 0; } -static struct link_data *find_next_link(struct mtp_link_set *set, - struct link_data *data) +static struct mtp_link *find_next_link(struct mtp_link_set *set, + struct mtp_link *data) { int found = 0; - struct link_data *next; + struct mtp_link *next; if (llist_empty(&set->links)) return NULL; @@ -581,7 +581,7 @@ static struct link_data *find_next_link(struct mtp_link_set *set, void mtp_link_set_init_slc(struct mtp_link_set *set) { - struct link_data *link = NULL; + struct mtp_link *link = NULL; int i; @@ -591,7 +591,7 @@ void mtp_link_set_init_slc(struct mtp_link_set *set) } } -void mtp_link_set_add_link(struct mtp_link_set *set, struct link_data *lnk) +void mtp_link_set_add_link(struct mtp_link_set *set, struct mtp_link *lnk) { llist_add_tail(&lnk->entry, &set->links); mtp_link_set_init_slc(set); -- cgit v1.2.3 From d70a7e8e79a25ecd1ba36e357a83c902338648b9 Mon Sep 17 00:00:00 2001 From: Holger Hans Peter Freyther Date: Mon, 17 Jan 2011 14:13:29 +0100 Subject: m2ua: Create a MTP Link class using SCTP/M2UA for the transport This is a MTP Link that can be used with a MTP LinkSet. E.g. with some config changes one could use cellmgr_ng or udt_relay over a M2UA link. --- configure.ac | 2 + include/Makefile.am | 2 +- include/sctp_m2ua.h | 60 +++++ src/Makefile.am | 4 +- src/sctp_m2ua.c | 631 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 696 insertions(+), 3 deletions(-) create mode 100644 include/sctp_m2ua.h create mode 100644 src/sctp_m2ua.c diff --git a/configure.ac b/configure.ac index dd2b04a..7651309 100644 --- a/configure.ac +++ b/configure.ac @@ -26,6 +26,8 @@ PKG_CHECK_MODULES([LIBOSMOSCCP], [libosmo-sccp]) PKG_CHECK_MODULES([LIBOSMOVTY], [libosmovty]) #PKG_CHECK_MODULES([NEXUSWARE_C7], [nexusware-c7]) +AC_CHECK_LIB([sctp], sctp_sendmsg, [], [AC_MSG_ERROR([The sctp library is required.])]) + AC_ARG_ENABLE([uniporte], [AS_HELP_STRING([--enable-uniporte], [Build with uniporte])], [ PKG_CHECK_MODULES([NEXUSWARE_UNIPORTE], [nexusware-uniporte]) diff --git a/include/Makefile.am b/include/Makefile.am index a0a54b9..6771b84 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,5 +1,5 @@ noinst_HEADERS = mtp_level3.h mtp_data.h ipaccess.h thread.h mtp_pcap.h \ mgcp_ss7.h bss_patch.h bssap_sccp.h bsc_data.h udp_input.h \ - snmp_mtp.h cellmgr_debug.h bsc_sccp.h bsc_ussd.h + snmp_mtp.h cellmgr_debug.h bsc_sccp.h bsc_ussd.h sctp_m2ua.h SUBDIRS = mgcp diff --git a/include/sctp_m2ua.h b/include/sctp_m2ua.h new file mode 100644 index 0000000..2627d7d --- /dev/null +++ b/include/sctp_m2ua.h @@ -0,0 +1,60 @@ +/* Run M2UA over SCTP here */ +/* (C) 2011 by Holger Hans Peter Freyther + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef sctp_m2ua_h +#define sctp_m2ua_h + +#include "mtp_data.h" + +#include +#include + +#include +#include + +struct sctp_m2ua_conn; +struct mtp_link; + +/** + * Drive M2UA over a SCTP link. Right now we have no + * real concept for failover and such for the link. + */ +struct mtp_m2ua_link { + struct mtp_link base; + + int started; + struct llist_head conns; + struct bsc_fd bsc; +}; + +/* + * One ASP that can be active or such. + */ +struct sctp_m2ua_conn { + struct llist_head entry; + uint8_t asp_ident[4]; + int asp_up; + int asp_active; + int established; + + struct write_queue queue; + struct mtp_m2ua_link *trans; +}; + +struct mtp_m2ua_link *sctp_m2ua_transp_create(const char *ip, int port); + +#endif diff --git a/src/Makefile.am b/src/Makefile.am index 83acf8d..0331406 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -17,6 +17,6 @@ cellmgr_ng_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) $( udt_relay_SOURCES = main_udt.c mtp_layer3.c thread.c input/ipaccess.c pcap.c \ msc_conn.c link_udp.c snmp_mtp.c debug.c vty_interface.c \ - bss_patch.c isup.c links.c + bss_patch.c isup.c links.c sctp_m2ua.c udt_relay_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOSCCP_LIBS) $(LIBOSMOVTY_LIBS) $(NEXUSWARE_C7_LIBS) \ - -lpthread -lnetsnmp -lcrypto + -lpthread -lnetsnmp -lcrypto -lm2ua -lsctp diff --git a/src/sctp_m2ua.c b/src/sctp_m2ua.c new file mode 100644 index 0000000..2ae7316 --- /dev/null +++ b/src/sctp_m2ua.c @@ -0,0 +1,631 @@ +/* Run M2UA over SCTP here */ +/* (C) 2011 by Holger Hans Peter Freyther + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +extern struct bsc_data bsc; + +static void m2ua_conn_destroy(struct sctp_m2ua_conn *conn) +{ + close(conn->queue.bfd.fd); + bsc_unregister_fd(&conn->queue.bfd); + write_queue_clear(&conn->queue); + llist_del(&conn->entry); + + if (conn->asp_up && conn->asp_active && conn->established) + mtp_link_down(&conn->trans->base); + talloc_free(conn); + + #warning "Notify any other AS(P) for failover scenario" +} + +static int m2ua_conn_send(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct msgb *msg; + msg = m2ua_to_msg(m2ua); + if (!msg) + return -1; + + /* save the OOB data in front of the message */ + msg->l2h = msg->data; + msgb_push(msg, sizeof(*info)); + memcpy(msg->data, info, sizeof(*info)); + + if (write_queue_enqueue(&conn->queue, msg) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to enqueue.\n"); + msgb_free(msg); + return -1; + } + + return 0; +} + +static int m2ua_conn_send_ntfy(struct sctp_m2ua_conn *conn, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg *msg; + uint16_t state[2]; + int rc; + + msg = m2ua_msg_alloc(); + if (!msg) + return -1; + msg->hdr.msg_class = M2UA_CLS_MGMT; + msg->hdr.msg_type = M2UA_MGMT_NTFY; + + /* state change */ + state[0] = ntohs(M2UA_STP_AS_STATE_CHG); + + if (conn->asp_active) + state[1] = ntohs(M2UA_STP_AS_ACTIVE); + else + state[1] = ntohs(M2UA_STP_AS_INACTIVE); + + m2ua_msg_add_data(msg, MUA_TAG_STATUS, 4, (uint8_t *) state); + m2ua_msg_add_data(msg, MUA_TAG_ASP_IDENT, 4, conn->asp_ident); + rc = m2ua_conn_send(conn, msg, info); + m2ua_msg_free(msg); + + return rc; +} + +static int m2ua_handle_asp_ack(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg_part *asp_ident; + struct m2ua_msg *ack; + + asp_ident = m2ua_msg_find_tag(m2ua, MUA_TAG_ASP_IDENT); + if (!asp_ident) { + LOGP(DINP, LOGL_ERROR, "ASP UP lacks ASP IDENT\n"); + return -1; + } + if (asp_ident->len != 4) { + LOGP(DINP, LOGL_ERROR, "ASP Ident needs to be four byte.\n"); + return -1; + } + + /* TODO: Better handling for fail over is needed here */ + ack = m2ua_msg_alloc(); + if (!ack) { + LOGP(DINP, LOGL_ERROR, "Failed to create response\n"); + return -1; + } + + ack->hdr.msg_class = M2UA_CLS_ASPSM; + ack->hdr.msg_type = M2UA_ASPSM_UP_ACK; + if (m2ua_conn_send(conn, ack, info) != 0) { + m2ua_msg_free(ack); + return -1; + } + + memcpy(conn->asp_ident, asp_ident->dat, 4); + conn->asp_up = 1; + + m2ua_conn_send_ntfy(conn, info); + m2ua_msg_free(ack); + return 0; +} + +static int m2ua_handle_asp(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, struct sctp_sndrcvinfo *info) +{ + switch (m2ua->hdr.msg_type) { + case M2UA_ASPSM_UP: + m2ua_handle_asp_ack(conn, m2ua, info); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unhandled msg_type %d\n", + m2ua->hdr.msg_type); + break; + } + + return 0; +} + +static int m2ua_handle_asptm_act(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg *ack; + + /* TODO: parse the interface identifiers. This is plural */ + ack = m2ua_msg_alloc(); + if (!ack) + return -1; + + ack->hdr.msg_class = M2UA_CLS_ASPTM; + ack->hdr.msg_type = M2UA_ASPTM_ACTIV_ACK; + + if (m2ua_conn_send(conn, ack, info) != 0) { + m2ua_msg_free(ack); + return -1; + } + + conn->asp_active = 1; + m2ua_conn_send_ntfy(conn, info); + m2ua_msg_free(ack); + return 0; +} + +static int m2ua_handle_asptm(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + switch (m2ua->hdr.msg_type) { + case M2UA_ASPTM_ACTIV: + m2ua_handle_asptm_act(conn, m2ua, info); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unhandled msg_type %d\n", + m2ua->hdr.msg_type); + break; + } + + return 0; +} + +static int m2ua_handle_state_req(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg_part *ident, *state; + struct m2ua_msg *conf; + int interface = 0, req; + + state = m2ua_msg_find_tag(m2ua, M2UA_TAG_STATE_REQ); + if (!state || state->len != 4) { + LOGP(DINP, LOGL_ERROR, "Mandantory state request not present.\n"); + return -1; + } + + ident = m2ua_msg_find_tag(m2ua, MUA_TAG_IDENT_INT); + if (ident && ident->len == 4) { + memcpy(&interface, ident->dat, 4); + interface = ntohl(interface); + } + + memcpy(&req, state->dat, 4); + req = ntohl(req); + + switch (req) { + case M2UA_STATUS_EMER_SET: + conf = m2ua_msg_alloc(); + if (!conf) + return -1; + + conf->hdr.msg_class = M2UA_CLS_MAUP; + conf->hdr.msg_type = M2UA_MAUP_STATE_CON; + m2ua_msg_add_data(conf, MUA_TAG_IDENT_INT, 4, (uint8_t *) &interface); + m2ua_msg_add_data(conf, M2UA_TAG_STATE_REQ, 4, (uint8_t *) &req); + if (m2ua_conn_send(conn, conf, info) != 0) { + m2ua_msg_free(conf); + return -1; + } + m2ua_msg_free(conf); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unknown STATE Request: %d\n", req); + break; + } + + return 0; +} + +static int m2ua_handle_est_req(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg *conf; + + conf = m2ua_msg_alloc(); + if (!conf) + return -1; + + conf->hdr.msg_class = M2UA_CLS_MAUP; + conf->hdr.msg_type = M2UA_MAUP_EST_CON; + + if (m2ua_conn_send(conn, conf, info) != 0) { + m2ua_msg_free(conf); + return -1; + } + + conn->established = 1; + LOGP(DINP, LOGL_NOTICE, "M2UA/Link is established.\n"); + mtp_link_up(&conn->trans->base); + m2ua_msg_free(conf); + return 0; +} + +static int m2ua_handle_rel_req(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg *conf; + + conf = m2ua_msg_alloc(); + if (!conf) + return -1; + + conf->hdr.msg_class = M2UA_CLS_MAUP; + conf->hdr.msg_type = M2UA_MAUP_REL_CON; + + if (m2ua_conn_send(conn, conf, info) != 0) { + m2ua_msg_free(conf); + return -1; + } + + conn->established = 0; + LOGP(DINP, LOGL_NOTICE, "M2UA/Link is released.\n"); + mtp_link_down(&conn->trans->base); + m2ua_msg_free(conf); + return 0; +} + +static int m2ua_handle_data(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + struct msgb *msg; + struct m2ua_msg_part *data; + + data = m2ua_msg_find_tag(m2ua, M2UA_TAG_DATA); + if (!data) { + LOGP(DINP, LOGL_ERROR, "No DATA in DATA message.\n"); + return -1; + } + + if (data->len > 2048) { + LOGP(DINP, LOGL_ERROR, "TOO much data for us to handle.\n"); + return -1; + } + + msg = msgb_alloc(2048, "m2ua-data"); + if (!msg) { + LOGP(DINP, LOGL_ERROR, "Failed to allocate storage.\n"); + return -1; + } + + msg->l2h = msgb_put(msg, data->len); + memcpy(msg->l2h, data->dat, data->len); + mtp_link_set_data(conn->trans->base.the_link, msg); + msgb_free(msg); + + return 0; +} + +static int m2ua_handle_maup(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, + struct sctp_sndrcvinfo *info) +{ + switch (m2ua->hdr.msg_type) { + case M2UA_MAUP_STATE_REQ: + m2ua_handle_state_req(conn, m2ua, info); + break; + case M2UA_MAUP_EST_REQ: + m2ua_handle_est_req(conn, m2ua, info); + break; + case M2UA_MAUP_REL_REQ: + m2ua_handle_rel_req(conn, m2ua, info); + break; + case M2UA_MAUP_DATA: + m2ua_handle_data(conn, m2ua, info); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unhandled msg_type %d\n", + m2ua->hdr.msg_type); + break; + } + + return 0; +} + +static int m2ua_handle_mgmt(struct sctp_m2ua_conn *conn, + struct m2ua_msg *m2ua, struct sctp_sndrcvinfo *info) +{ + switch (m2ua->hdr.msg_type) { + case M2UA_MGMT_ERROR: + LOGP(DINP, LOGL_ERROR, "We did something wrong. Error...\n"); + break; + case M2UA_MGMT_NTFY: + LOGP(DINP, LOGL_NOTICE, "There was a notiy.. but we should only send it.\n"); + break; + } + + return 0; +} + +static int m2ua_conn_handle(struct sctp_m2ua_conn *conn, + struct msgb *msg, struct sctp_sndrcvinfo *info) +{ + struct m2ua_msg *m2ua; + m2ua = m2ua_from_msg(msg->len, msg->data); + if (!m2ua) { + LOGP(DINP, LOGL_ERROR, "Failed to parse the message.\n"); + return -1; + } + + switch (m2ua->hdr.msg_class) { + case M2UA_CLS_MGMT: + m2ua_handle_mgmt(conn, m2ua, info); + break; + case M2UA_CLS_ASPSM: + m2ua_handle_asp(conn, m2ua, info); + break; + case M2UA_CLS_ASPTM: + m2ua_handle_asptm(conn, m2ua, info); + break; + case M2UA_CLS_MAUP: + m2ua_handle_maup(conn, m2ua, info); + break; + default: + LOGP(DINP, LOGL_ERROR, "Unhandled msg_class %d\n", + m2ua->hdr.msg_class); + break; + } + + m2ua_msg_free(m2ua); + return 0; +} + +static int m2ua_conn_read(struct bsc_fd *fd) +{ + struct sockaddr_in addr; + struct sctp_sndrcvinfo info; + socklen_t len = sizeof(addr); + struct msgb *msg; + int rc; + + msg = msgb_alloc(2048, "m2ua buffer"); + if (!msg) { + LOGP(DINP, LOGL_ERROR, "Failed to allocate buffer.\n"); + m2ua_conn_destroy(fd->data); + return -1; + } + + memset(&info, 0, sizeof(info)); + memset(&addr, 0, sizeof(addr)); + rc = sctp_recvmsg(fd->fd, msg->data, msg->data_len, + (struct sockaddr *) &addr, &len, &info, NULL); + if (rc < 0) { + LOGP(DINP, LOGL_ERROR, "Failed to read.\n"); + m2ua_conn_destroy(fd->data); + return -1; + } + + msgb_put(msg, rc); + LOGP(DINP, LOGL_NOTICE, "Read %d on stream: %d ssn: %d assoc: %d\n", + rc, info.sinfo_stream, info.sinfo_ssn, info.sinfo_assoc_id); + m2ua_conn_handle(fd->data, msg, &info); + msgb_free(msg); + return 0; +} + +static int sctp_m2ua_write(struct mtp_link *link, struct msgb *msg) +{ + struct mtp_m2ua_link *trans; + struct sctp_m2ua_conn *conn = NULL, *tmp; + struct sctp_sndrcvinfo info; + struct m2ua_msg *m2ua; + uint32_t interface; + + trans = (struct mtp_m2ua_link *) link; + + if (llist_empty(&trans->conns)) + return -1; + + llist_for_each_entry(tmp, &trans->conns, entry) + if (tmp->established && tmp->asp_active && tmp->asp_up) { + conn = tmp; + break; + } + + if (!conn) { + LOGP(DINP, LOGL_ERROR, "No active ASP?\n"); + return -1; + } + + m2ua = m2ua_msg_alloc(); + if (!m2ua) + return -1; + + m2ua->hdr.msg_class = M2UA_CLS_MAUP; + m2ua->hdr.msg_type = M2UA_MAUP_DATA; + + interface = htonl(0); + m2ua_msg_add_data(m2ua, MUA_TAG_IDENT_INT, 4, (uint8_t *) &interface); + m2ua_msg_add_data(m2ua, M2UA_TAG_DATA, msg->len, msg->data); + + memset(&info, 0, sizeof(info)); + info.sinfo_stream = 1; + info.sinfo_assoc_id = 1; + info.sinfo_ppid = htonl(2); + + m2ua_conn_send(conn, m2ua, &info); + m2ua_msg_free(m2ua); + return 0; +} + +static int m2ua_conn_write(struct bsc_fd *fd, struct msgb *msg) +{ + int ret; + struct sctp_sndrcvinfo info; + memcpy(&info, msg->data, sizeof(info)); + + ret = sctp_send(fd->fd, msg->l2h, msgb_l2len(msg), + &info, 0); + + if (ret != msgb_l2len(msg)) + LOGP(DINP, LOGL_ERROR, "Failed to send %d.\n", ret); + + return 0; +} + +static int sctp_trans_accept(struct bsc_fd *fd, unsigned int what) +{ + struct sctp_event_subscribe events; + struct mtp_m2ua_link *trans; + struct sctp_m2ua_conn *conn; + struct sockaddr_in addr; + socklen_t len; + int s; + + len = sizeof(addr); + s = accept(fd->fd, (struct sockaddr *) &addr, &len); + if (s < 0) { + LOGP(DINP, LOGL_ERROR, "Failed to accept.\n"); + return -1; + } + + trans = fd->data; + if (!trans->started) { + LOGP(DINP, LOGL_NOTICE, "The link is not started.\n"); + close(s); + return -1; + } + + LOGP(DINP, LOGL_NOTICE, "Got a new SCTP connection.\n"); + conn = talloc_zero(fd->data, struct sctp_m2ua_conn); + if (!conn) { + LOGP(DINP, LOGL_ERROR, "Failed to create.\n"); + close(s); + return -1; + } + + conn->trans = trans; + + write_queue_init(&conn->queue, 10); + conn->queue.bfd.fd = s; + conn->queue.bfd.data = conn; + conn->queue.bfd.when = BSC_FD_READ; + conn->queue.read_cb = m2ua_conn_read; + conn->queue.write_cb = m2ua_conn_write; + + if (bsc_register_fd(&conn->queue.bfd) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to register.\n"); + close(s); + talloc_free(conn); + return -1; + } + + memset(&events, 0, sizeof(events)); + events.sctp_data_io_event = 1; + setsockopt(s, SOL_SCTP, SCTP_EVENTS, &events, sizeof(events)); + + llist_add_tail(&conn->entry, &trans->conns); + return 0; +} + +static int sctp_m2ua_dummy(struct mtp_link *link) +{ + return 0; +} + +static int sctp_m2ua_start(struct mtp_link *link) +{ + struct mtp_m2ua_link *trans = (struct mtp_m2ua_link *) link; + + trans->started = 1; + return 0; +} + +static int sctp_m2ua_reset(struct mtp_link *link) +{ + struct sctp_m2ua_conn *conn, *tmp; + struct mtp_m2ua_link *transp = (struct mtp_m2ua_link *) link; + + llist_for_each_entry_safe(conn, tmp, &transp->conns, entry) + m2ua_conn_destroy(conn); + + return 0; +} + +struct mtp_m2ua_link *sctp_m2ua_transp_create(const char *ip, int port) +{ + int sctp; + struct sockaddr_in addr; + struct mtp_m2ua_link *trans; + + sctp = socket(PF_INET, SOCK_STREAM, IPPROTO_SCTP); + if (!sctp) { + LOGP(DINP, LOGL_ERROR, "Failed to create socket.\n"); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = inet_addr(ip); + + if (bind(sctp, (struct sockaddr *) &addr, sizeof(addr)) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to bind.\n"); + close(sctp); + return NULL; + } + + if (listen(sctp, 1) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to listen.\n"); + close(sctp); + return NULL; + } + + int on = 1; + setsockopt(sctp, SOL_SCTP, 112, &on, sizeof(on)); + + trans = talloc_zero(NULL, struct mtp_m2ua_link); + if (!trans) { + LOGP(DINP, LOGL_ERROR, "Remove the talloc.\n"); + close(sctp); + return NULL; + } + + trans->base.shutdown = sctp_m2ua_dummy; + trans->base.clear_queue = sctp_m2ua_dummy; + trans->base.reset = sctp_m2ua_reset; + trans->base.start = sctp_m2ua_start; + trans->base.write = sctp_m2ua_write; + + trans->bsc.fd = sctp; + trans->bsc.data = trans; + trans->bsc.cb = sctp_trans_accept; + trans->bsc.when = BSC_FD_READ; + + if (bsc_register_fd(&trans->bsc) != 0) { + LOGP(DINP, LOGL_ERROR, "Failed to register the fd.\n"); + talloc_free(trans); + close(sctp); + return NULL; + } + + INIT_LLIST_HEAD(&trans->conns); + return trans; +} + -- cgit v1.2.3