blob: c85bf5bd5af1d1bbbe15609294e4046aa2db6129 [file] [log] [blame]
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +08001/* Routines to talk to the MSC using the IPA Protocol */
2/*
3 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
Holger Hans Peter Freytherdf6143a2010-06-15 18:46:56 +08004 * (C) 2010 by On-Waves
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +08005 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 */
22
23#include <openbsc/bsc_msc.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010024#include <openbsc/debug.h>
Holger Hans Peter Freytherf76e7ef2010-06-15 18:52:05 +080025#include <openbsc/ipaccess.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010026
27#include <osmocore/write_queue.h>
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080028#include <osmocore/talloc.h>
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080029
30#include <arpa/inet.h>
31#include <sys/socket.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010032#include <errno.h>
33#include <fcntl.h>
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080034#include <stdio.h>
35#include <string.h>
36#include <unistd.h>
37
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080038static void connection_loss(struct bsc_msc_connection *con)
39{
40 struct bsc_fd *fd;
41
42 fd = &con->write_queue.bfd;
43
44 close(fd->fd);
45 fd->fd = -1;
46 fd->cb = write_queue_bfd_cb;
47 fd->when = 0;
48
49 con->is_connected = 0;
Holger Hans Peter Freytherbec411b2010-07-05 14:14:18 +080050 con->first_contact = 0;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080051 con->connection_loss(con);
52}
53
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +080054static void msc_con_timeout(void *_con)
55{
56 struct bsc_msc_connection *con = _con;
57
58 LOGP(DMSC, LOGL_ERROR, "MSC Connection timeout.\n");
59 bsc_msc_lost(con);
60}
61
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +020062static int bsc_msc_except(struct bsc_fd *bfd)
63{
64 struct write_queue *wrt;
65 struct bsc_msc_connection *con;
66
67 LOGP(DMSC, LOGL_ERROR, "Exception on the BFD. Closing down.\n");
68
69 wrt = container_of(bfd, struct write_queue, bfd);
70 con = container_of(wrt, struct bsc_msc_connection, write_queue);
71
72 connection_loss(con);
73 return 0;
74}
75
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010076/* called in the case of a non blocking connect */
77static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
78{
79 int rc;
80 int val;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080081 struct bsc_msc_connection *con;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010082 struct write_queue *queue;
83
84 socklen_t len = sizeof(val);
85
86 if ((what & BSC_FD_WRITE) == 0) {
87 LOGP(DMSC, LOGL_ERROR, "Callback but not readable.\n");
88 return -1;
89 }
90
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080091 queue = container_of(fd, struct write_queue, bfd);
92 con = container_of(queue, struct bsc_msc_connection, write_queue);
93
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010094 /* check the socket state */
95 rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
96 if (rc != 0) {
97 LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC socket failed.\n");
98 goto error;
99 }
100 if (val != 0) {
Holger Hans Peter Freyther9d90da92010-04-06 10:25:00 +0200101 LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100102 goto error;
103 }
104
105
106 /* go to full operation */
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100107 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200108 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800109
110 con->is_connected = 1;
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +0800111 bsc_del_timer(&con->timeout_timer);
Holger Hans Peter Freytherb9ac37d2010-04-05 17:58:52 +0200112 LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800113 if (con->connected)
114 con->connected(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100115 return 0;
116
117error:
118 bsc_unregister_fd(fd);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800119 connection_loss(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100120 return -1;
121}
122static void setnonblocking(struct bsc_fd *fd)
123{
124 int flags;
125
126 flags = fcntl(fd->fd, F_GETFL);
127 if (flags < 0) {
128 perror("fcntl get failed");
129 close(fd->fd);
130 fd->fd = -1;
131 return;
132 }
133
134 flags |= O_NONBLOCK;
135 flags = fcntl(fd->fd, F_SETFL, flags);
136 if (flags < 0) {
137 perror("fcntl get failed");
138 close(fd->fd);
139 fd->fd = -1;
140 return;
141 }
142}
143
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800144int bsc_msc_connect(struct bsc_msc_connection *con)
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800145{
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800146 struct bsc_fd *fd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800147 struct sockaddr_in sin;
148 int on = 1, ret;
149
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800150 LOGP(DMSC, LOGL_NOTICE, "Attempting to connect MSC at %s:%d\n", con->ip, con->port);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800151
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800152 con->is_connected = 0;
153
154 fd = &con->write_queue.bfd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800155 fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800156 fd->data = NULL;
157 fd->priv_nr = 1;
158
159 if (fd->fd < 0) {
160 perror("Creating TCP socket failed");
161 return fd->fd;
162 }
163
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100164 /* make it non blocking */
165 setnonblocking(fd);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800166
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800167 /* set the socket priority */
168 ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS,
169 &con->prio, sizeof(con->prio));
170 if (ret != 0)
171 LOGP(DMSC, LOGL_ERROR, "Failed to set prio to %d. %s\n",
172 con->prio, strerror(errno));
173
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800174 memset(&sin, 0, sizeof(sin));
175 sin.sin_family = AF_INET;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800176 sin.sin_port = htons(con->port);
177 inet_aton(con->ip, &sin.sin_addr);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800178
179 setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
180 ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
181
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100182 if (ret == -1 && errno == EINPROGRESS) {
183 LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
184 fd->when = BSC_FD_WRITE;
185 fd->cb = msc_connection_connect;
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +0800186 con->timeout_timer.cb = msc_con_timeout;
187 con->timeout_timer.data = con;
188 bsc_schedule_timer(&con->timeout_timer, 20, 0);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100189 } else if (ret < 0) {
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800190 perror("Connection failed");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800191 connection_loss(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800192 return ret;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100193 } else {
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200194 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100195 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800196 con->is_connected = 1;
197 if (con->connected)
198 con->connected(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800199 }
200
201 ret = bsc_register_fd(fd);
202 if (ret < 0) {
203 perror("Registering the fd failed");
204 close(fd->fd);
205 return ret;
206 }
207
208 return ret;
209}
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800210
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800211struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio)
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800212{
213 struct bsc_msc_connection *con;
214
215 con = talloc_zero(NULL, struct bsc_msc_connection);
216 if (!con) {
217 LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n");
218 return NULL;
219 }
220
221 con->ip = ip;
222 con->port = port;
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800223 con->prio = prio;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800224 write_queue_init(&con->write_queue, 100);
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200225 con->write_queue.except_cb = bsc_msc_except;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800226 return con;
227}
228
229void bsc_msc_lost(struct bsc_msc_connection *con)
230{
Holger Hans Peter Freyther03ca97e2010-03-31 13:15:26 +0200231 write_queue_clear(&con->write_queue);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800232 bsc_unregister_fd(&con->write_queue.bfd);
233 connection_loss(con);
234}
235
236static void reconnect_msc(void *_msc)
237{
238 struct bsc_msc_connection *con = _msc;
239
240 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
241 bsc_msc_connect(con);
242}
243
244void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
245{
246 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
247 con->reconnect_timer.cb = reconnect_msc;
248 con->reconnect_timer.data = con;
249 bsc_schedule_timer(&con->reconnect_timer, 5, 0);
Holger Hans Peter Freytherf76e7ef2010-06-15 18:52:05 +0800250}
251
252struct msgb *bsc_msc_id_get_resp(const char *token)
253{
254 struct msgb *msg;
255
256 if (!token) {
257 LOGP(DMSC, LOGL_ERROR, "No token specified.\n");
258 return NULL;
259 }
260
261 msg = msgb_alloc_headroom(4096, 128, "id resp");
262 if (!msg) {
263 LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n");
264 return NULL;
265 }
266
267 msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
268 msgb_l16tv_put(msg, strlen(token) + 1,
269 IPAC_IDTAG_UNITNAME, (u_int8_t *) token);
270 return msg;
Holger Hans Peter Freytherbec411b2010-07-05 14:14:18 +0800271}