blob: 508697ab12a22ed074e5543cb2b077e7070a1249 [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
Harald Welte9af6ddf2011-01-01 15:25:50 +01008 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080010 * (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
Harald Welte9af6ddf2011-01-01 15:25:50 +010015 * GNU Affero General Public License for more details.
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080016 *
Harald Welte9af6ddf2011-01-01 15:25:50 +010017 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080019 *
20 */
21
22#include <openbsc/bsc_msc.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010023#include <openbsc/debug.h>
Holger Hans Peter Freytherf76e7ef2010-06-15 18:52:05 +080024#include <openbsc/ipaccess.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010025
26#include <osmocore/write_queue.h>
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080027#include <osmocore/talloc.h>
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080028
29#include <arpa/inet.h>
30#include <sys/socket.h>
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010031#include <errno.h>
32#include <fcntl.h>
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +080033#include <stdio.h>
34#include <string.h>
35#include <unistd.h>
36
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080037static void connection_loss(struct bsc_msc_connection *con)
38{
39 struct bsc_fd *fd;
40
41 fd = &con->write_queue.bfd;
42
43 close(fd->fd);
44 fd->fd = -1;
45 fd->cb = write_queue_bfd_cb;
46 fd->when = 0;
47
48 con->is_connected = 0;
Holger Hans Peter Freytherbec411b2010-07-05 14:14:18 +080049 con->first_contact = 0;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080050 con->connection_loss(con);
51}
52
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +080053static void msc_con_timeout(void *_con)
54{
55 struct bsc_msc_connection *con = _con;
56
57 LOGP(DMSC, LOGL_ERROR, "MSC Connection timeout.\n");
58 bsc_msc_lost(con);
59}
60
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010061/* called in the case of a non blocking connect */
62static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
63{
64 int rc;
65 int val;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080066 struct bsc_msc_connection *con;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010067 struct write_queue *queue;
68
69 socklen_t len = sizeof(val);
70
71 if ((what & BSC_FD_WRITE) == 0) {
Holger Hans Peter Freyther62abade2010-08-04 02:27:34 +080072 LOGP(DMSC, LOGL_ERROR, "Callback but not writable.\n");
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010073 return -1;
74 }
75
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080076 queue = container_of(fd, struct write_queue, bfd);
77 con = container_of(queue, struct bsc_msc_connection, write_queue);
78
Holger Hans Peter Freytherd4eed522010-10-07 04:42:03 +080079 /* From here on we will either be connected or reconnect */
80 bsc_del_timer(&con->timeout_timer);
81
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010082 /* check the socket state */
83 rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
84 if (rc != 0) {
85 LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC socket failed.\n");
86 goto error;
87 }
88 if (val != 0) {
Holger Hans Peter Freyther9d90da92010-04-06 10:25:00 +020089 LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010090 goto error;
91 }
92
93
94 /* go to full operation */
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010095 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +020096 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080097
98 con->is_connected = 1;
Holger Hans Peter Freytherb9ac37d2010-04-05 17:58:52 +020099 LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800100 if (con->connected)
101 con->connected(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100102 return 0;
103
104error:
105 bsc_unregister_fd(fd);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800106 connection_loss(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100107 return -1;
108}
109static void setnonblocking(struct bsc_fd *fd)
110{
111 int flags;
112
113 flags = fcntl(fd->fd, F_GETFL);
114 if (flags < 0) {
115 perror("fcntl get failed");
116 close(fd->fd);
117 fd->fd = -1;
118 return;
119 }
120
121 flags |= O_NONBLOCK;
122 flags = fcntl(fd->fd, F_SETFL, flags);
123 if (flags < 0) {
124 perror("fcntl get failed");
125 close(fd->fd);
126 fd->fd = -1;
127 return;
128 }
129}
130
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800131int bsc_msc_connect(struct bsc_msc_connection *con)
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800132{
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800133 struct bsc_fd *fd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800134 struct sockaddr_in sin;
135 int on = 1, ret;
136
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800137 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 +0800138
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800139 con->is_connected = 0;
140
141 fd = &con->write_queue.bfd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800142 fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800143 fd->priv_nr = 1;
144
145 if (fd->fd < 0) {
146 perror("Creating TCP socket failed");
147 return fd->fd;
148 }
149
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100150 /* make it non blocking */
151 setnonblocking(fd);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800152
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800153 /* set the socket priority */
154 ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS,
155 &con->prio, sizeof(con->prio));
156 if (ret != 0)
157 LOGP(DMSC, LOGL_ERROR, "Failed to set prio to %d. %s\n",
158 con->prio, strerror(errno));
159
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800160 memset(&sin, 0, sizeof(sin));
161 sin.sin_family = AF_INET;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800162 sin.sin_port = htons(con->port);
163 inet_aton(con->ip, &sin.sin_addr);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800164
165 setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
166 ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
167
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100168 if (ret == -1 && errno == EINPROGRESS) {
169 LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
170 fd->when = BSC_FD_WRITE;
171 fd->cb = msc_connection_connect;
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +0800172 con->timeout_timer.cb = msc_con_timeout;
173 con->timeout_timer.data = con;
174 bsc_schedule_timer(&con->timeout_timer, 20, 0);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100175 } else if (ret < 0) {
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800176 perror("Connection failed");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800177 connection_loss(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800178 return ret;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100179 } else {
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200180 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100181 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800182 con->is_connected = 1;
183 if (con->connected)
184 con->connected(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800185 }
186
187 ret = bsc_register_fd(fd);
188 if (ret < 0) {
189 perror("Registering the fd failed");
190 close(fd->fd);
191 return ret;
192 }
193
194 return ret;
195}
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800196
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800197struct bsc_msc_connection *bsc_msc_create(const char *ip, int port, int prio)
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800198{
199 struct bsc_msc_connection *con;
200
201 con = talloc_zero(NULL, struct bsc_msc_connection);
202 if (!con) {
203 LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n");
204 return NULL;
205 }
206
207 con->ip = ip;
208 con->port = port;
Holger Hans Peter Freytherca999a92010-06-15 18:52:38 +0800209 con->prio = prio;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800210 write_queue_init(&con->write_queue, 100);
211 return con;
212}
213
214void bsc_msc_lost(struct bsc_msc_connection *con)
215{
Holger Hans Peter Freyther03ca97e2010-03-31 13:15:26 +0200216 write_queue_clear(&con->write_queue);
Holger Hans Peter Freytherd4eed522010-10-07 04:42:03 +0800217 bsc_del_timer(&con->timeout_timer);
Holger Hans Peter Freytherfad07532010-10-07 06:07:57 +0800218
219 if (con->write_queue.bfd.fd >= 0)
220 bsc_unregister_fd(&con->write_queue.bfd);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800221 connection_loss(con);
222}
223
224static void reconnect_msc(void *_msc)
225{
226 struct bsc_msc_connection *con = _msc;
227
228 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
229 bsc_msc_connect(con);
230}
231
232void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
233{
234 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
235 con->reconnect_timer.cb = reconnect_msc;
236 con->reconnect_timer.data = con;
237 bsc_schedule_timer(&con->reconnect_timer, 5, 0);
Holger Hans Peter Freytherf76e7ef2010-06-15 18:52:05 +0800238}
239
240struct msgb *bsc_msc_id_get_resp(const char *token)
241{
242 struct msgb *msg;
243
244 if (!token) {
245 LOGP(DMSC, LOGL_ERROR, "No token specified.\n");
246 return NULL;
247 }
248
249 msg = msgb_alloc_headroom(4096, 128, "id resp");
250 if (!msg) {
251 LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n");
252 return NULL;
253 }
254
255 msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP);
256 msgb_l16tv_put(msg, strlen(token) + 1,
257 IPAC_IDTAG_UNITNAME, (u_int8_t *) token);
258 return msg;
Holger Hans Peter Freytherbec411b2010-07-05 14:14:18 +0800259}