blob: 2902f2903f5afe25e968d123741e27556247eff2 [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>
25
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;
49 con->connection_loss(con);
50}
51
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +080052static void msc_con_timeout(void *_con)
53{
54 struct bsc_msc_connection *con = _con;
55
56 LOGP(DMSC, LOGL_ERROR, "MSC Connection timeout.\n");
57 bsc_msc_lost(con);
58}
59
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +020060static int bsc_msc_except(struct bsc_fd *bfd)
61{
62 struct write_queue *wrt;
63 struct bsc_msc_connection *con;
64
65 LOGP(DMSC, LOGL_ERROR, "Exception on the BFD. Closing down.\n");
66
67 wrt = container_of(bfd, struct write_queue, bfd);
68 con = container_of(wrt, struct bsc_msc_connection, write_queue);
69
70 connection_loss(con);
71 return 0;
72}
73
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010074/* called in the case of a non blocking connect */
75static int msc_connection_connect(struct bsc_fd *fd, unsigned int what)
76{
77 int rc;
78 int val;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080079 struct bsc_msc_connection *con;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010080 struct write_queue *queue;
81
82 socklen_t len = sizeof(val);
83
84 if ((what & BSC_FD_WRITE) == 0) {
85 LOGP(DMSC, LOGL_ERROR, "Callback but not readable.\n");
86 return -1;
87 }
88
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +080089 queue = container_of(fd, struct write_queue, bfd);
90 con = container_of(queue, struct bsc_msc_connection, write_queue);
91
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +010092 /* check the socket state */
93 rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len);
94 if (rc != 0) {
95 LOGP(DMSC, LOGL_ERROR, "getsockopt for the MSC socket failed.\n");
96 goto error;
97 }
98 if (val != 0) {
Holger Hans Peter Freyther9d90da92010-04-06 10:25:00 +020099 LOGP(DMSC, LOGL_ERROR, "Not connected to the MSC: %d\n", val);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100100 goto error;
101 }
102
103
104 /* go to full operation */
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100105 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200106 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800107
108 con->is_connected = 1;
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +0800109 bsc_del_timer(&con->timeout_timer);
Holger Hans Peter Freytherb9ac37d2010-04-05 17:58:52 +0200110 LOGP(DMSC, LOGL_NOTICE, "(Re)Connected to the MSC.\n");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800111 if (con->connected)
112 con->connected(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100113 return 0;
114
115error:
116 bsc_unregister_fd(fd);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800117 connection_loss(con);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100118 return -1;
119}
120static void setnonblocking(struct bsc_fd *fd)
121{
122 int flags;
123
124 flags = fcntl(fd->fd, F_GETFL);
125 if (flags < 0) {
126 perror("fcntl get failed");
127 close(fd->fd);
128 fd->fd = -1;
129 return;
130 }
131
132 flags |= O_NONBLOCK;
133 flags = fcntl(fd->fd, F_SETFL, flags);
134 if (flags < 0) {
135 perror("fcntl get failed");
136 close(fd->fd);
137 fd->fd = -1;
138 return;
139 }
140}
141
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800142int bsc_msc_connect(struct bsc_msc_connection *con)
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800143{
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800144 struct bsc_fd *fd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800145 struct sockaddr_in sin;
146 int on = 1, ret;
147
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800148 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 +0800149
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800150 con->is_connected = 0;
151
152 fd = &con->write_queue.bfd;
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800153 fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800154 fd->data = NULL;
155 fd->priv_nr = 1;
156
157 if (fd->fd < 0) {
158 perror("Creating TCP socket failed");
159 return fd->fd;
160 }
161
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100162 /* make it non blocking */
163 setnonblocking(fd);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800164
165 memset(&sin, 0, sizeof(sin));
166 sin.sin_family = AF_INET;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800167 sin.sin_port = htons(con->port);
168 inet_aton(con->ip, &sin.sin_addr);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800169
170 setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
171 ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin));
172
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100173 if (ret == -1 && errno == EINPROGRESS) {
174 LOGP(DMSC, LOGL_ERROR, "MSC Connection in progress\n");
175 fd->when = BSC_FD_WRITE;
176 fd->cb = msc_connection_connect;
Holger Hans Peter Freythere47a91b2010-05-05 22:48:56 +0800177 con->timeout_timer.cb = msc_con_timeout;
178 con->timeout_timer.data = con;
179 bsc_schedule_timer(&con->timeout_timer, 20, 0);
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100180 } else if (ret < 0) {
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800181 perror("Connection failed");
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800182 connection_loss(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800183 return ret;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100184 } else {
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200185 fd->when = BSC_FD_READ | BSC_FD_EXCEPT;
Holger Hans Peter Freyther6c0a04e2010-03-26 10:41:20 +0100186 fd->cb = write_queue_bfd_cb;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800187 con->is_connected = 1;
188 if (con->connected)
189 con->connected(con);
Holger Hans Peter Freythereef86b52010-06-15 18:45:06 +0800190 }
191
192 ret = bsc_register_fd(fd);
193 if (ret < 0) {
194 perror("Registering the fd failed");
195 close(fd->fd);
196 return ret;
197 }
198
199 return ret;
200}
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800201
202struct bsc_msc_connection *bsc_msc_create(const char *ip, int port)
203{
204 struct bsc_msc_connection *con;
205
206 con = talloc_zero(NULL, struct bsc_msc_connection);
207 if (!con) {
208 LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n");
209 return NULL;
210 }
211
212 con->ip = ip;
213 con->port = port;
214 write_queue_init(&con->write_queue, 100);
Holger Hans Peter Freyther0176eb42010-04-08 11:12:32 +0200215 con->write_queue.except_cb = bsc_msc_except;
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800216 return con;
217}
218
219void bsc_msc_lost(struct bsc_msc_connection *con)
220{
Holger Hans Peter Freyther03ca97e2010-03-31 13:15:26 +0200221 write_queue_clear(&con->write_queue);
Holger Hans Peter Freytherbaf2abe2010-06-15 18:47:29 +0800222 bsc_unregister_fd(&con->write_queue.bfd);
223 connection_loss(con);
224}
225
226static void reconnect_msc(void *_msc)
227{
228 struct bsc_msc_connection *con = _msc;
229
230 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
231 bsc_msc_connect(con);
232}
233
234void bsc_msc_schedule_connect(struct bsc_msc_connection *con)
235{
236 LOGP(DMSC, LOGL_NOTICE, "Attempting to reconnect to the MSC.\n");
237 con->reconnect_timer.cb = reconnect_msc;
238 con->reconnect_timer.data = con;
239 bsc_schedule_timer(&con->reconnect_timer, 5, 0);
240}