blob: 9e728bc14ea4128b06b87919a192276eeeed7715 [file] [log] [blame]
Harald Welte33272882010-12-23 00:02:51 +01001/* mncc_sock.c: Tie the MNCC interface to a unix domain socket */
2
3/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org>
4 * (C) 2009 by Andreas Eversberg <Andreas.Eversberg@versatel.de>
5 * 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 <stdio.h>
24#include <unistd.h>
25#include <stdlib.h>
26#include <string.h>
27#include <errno.h>
28#include <sys/types.h>
29#include <sys/socket.h>
30#include <sys/un.h>
31
32#include <osmocore/talloc.h>
33#include <osmocore/select.h>
34
35#include <openbsc/debug.h>
36#include <openbsc/mncc.h>
37#include <openbsc/gsm_data.h>
38
39struct mncc_sock_state {
40 struct gsm_network *net;
41 struct bsc_fd listen_bfd; /* fd for listen socket */
42 struct bsc_fd conn_bfd; /* fd for connection to lcr */
43};
44
45/* FIXME: avoid this */
46static struct mncc_sock_state *g_state;
47
48void mncc_sock_write_pending(void)
49{
50 g_state->conn_bfd.when |= BSC_FD_WRITE;
51}
52
53/* FIXME: move this to libosmocore */
54int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path);
55
56static void mncc_sock_close(struct mncc_sock_state *state)
57{
58 struct bsc_fd *bfd = &state->conn_bfd;
59
60 close(bfd->fd);
61 bfd->fd = -1;
62 bsc_unregister_fd(bfd);
63
64 /* re-enable the generation of ACCEPT for new connections */
65 state->listen_bfd.when |= BSC_FD_READ;
66
67 /* FIXME: make sure we don't enqueue anymore */
68 /* FIXME: release all exisitng calls */
69
70 /* flush the queue */
71 while (!llist_empty(&state->net->upqueue)) {
72 struct msgb *msg = msgb_dequeue(&state->net->upqueue);
73 msgb_free(msg);
74 }
75}
76
77static int mncc_sock_read(struct bsc_fd *bfd)
78{
79 struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data;
80 struct gsm_mncc *mncc_prim;
81 struct msgb *msg;
82 int rc;
83
84 msg = msgb_alloc(sizeof(*mncc_prim)+256, "mncc_sock_rx");
85 if (!msg)
86 return -ENOMEM;
87
88 mncc_prim = (struct gsm_mncc *) msg->tail;
89
90 rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
91 if (rc == 0)
92 goto close;
93
94 if (rc < 0) {
95 if (errno == EAGAIN)
96 return 0;
97 fprintf(stderr, "Err from socket: %s\n", strerror(errno));
98 goto close;
99 }
100
101 rc = mncc_tx_to_cc(state->net, mncc_prim->msg_type, mncc_prim);
102
103 /* as we always synchronously process the message in mncc_send() and
104 * its callbacks, we can free the message here. */
105 msgb_free(msg);
106
107 return rc;
108
109close:
110 mncc_sock_close(state);
111 return -1;
112}
113
114static int mncc_sock_write(struct bsc_fd *bfd)
115{
116 struct mncc_sock_state *state = bfd->data;
117 struct gsm_network *net = state->net;
118 int rc;
119
120 while (!llist_empty(&net->upqueue)) {
121 struct msgb *msg;
122 struct gsm_mncc *mncc_prim;
123
124 /* peek at the beginning of the queue */
125 msg = llist_entry(net->upqueue.next, struct msgb, list);
126 mncc_prim = (struct gsm_mncc *)msg->data;
127
128 bfd->when &= ~BSC_FD_WRITE;
129
130 /* try to send it over the socket */
131 rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
132 if (rc == 0)
133 goto close;
134 if (rc < 0) {
135 if (errno == EAGAIN) {
136 bfd->when |= BSC_FD_WRITE;
137 break;
138 }
139 goto close;
140 }
141 /* _after_ we send it, we can deueue */
142 msgb_dequeue(&net->upqueue);
143 }
144 return 0;
145
146close:
147 mncc_sock_close(state);
148
149 return -1;
150}
151
152static int mncc_sock_cb(struct bsc_fd *bfd, unsigned int flags)
153{
154 int rc = 0;
155
156 if (flags & BSC_FD_READ)
157 rc = mncc_sock_read(bfd);
158 if (rc < 0)
159 return rc;
160
161 if (flags & BSC_FD_WRITE)
162 rc = mncc_sock_write(bfd);
163
164 return rc;
165}
166
167/* accept a new connection */
168static int mncc_sock_accept(struct bsc_fd *bfd, unsigned int flags)
169{
170 struct mncc_sock_state *state = (struct mncc_sock_state *)bfd->data;
171 struct bsc_fd *conn_bfd = &state->conn_bfd;
172 struct sockaddr_un un_addr;
173 socklen_t len;
174 int rc;
175
176 len = sizeof(un_addr);
177 rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
178 if (rc < 0) {
179 fprintf(stderr, "Failed to accept a new connection.\n");
180 return -1;
181 }
182
183 if (conn_bfd->fd > 0) {
184 /* We already have one MNCC app connected, this is all we support */
185 state->listen_bfd.when &= ~BSC_FD_READ;
186 close(rc);
187 }
188
189 conn_bfd->fd = rc;
190 conn_bfd->when = BSC_FD_READ;
191 conn_bfd->cb = mncc_sock_cb;
192 conn_bfd->data = state;
193
194 if (bsc_register_fd(conn_bfd) != 0) {
195 fprintf(stderr, "Failed to register the fd.\n");
196 close(conn_bfd->fd);
197 conn_bfd->fd = -1;
198 state->listen_bfd.when |= ~BSC_FD_READ;
199 return -1;
200 }
201
202 return 0;
203}
204
205
206int mncc_sock_init(struct gsm_network *net)
207{
208 struct mncc_sock_state *state;
209 struct bsc_fd *bfd;
210 int rc;
211
212 state = talloc_zero(tall_bsc_ctx, struct mncc_sock_state);
213 if (!state)
214 return -ENOMEM;
215
216 state->net = net;
217 state->conn_bfd.fd = -1;
218
219 bfd = &state->listen_bfd;
220
221 rc = osmo_unixsock_listen(bfd, SOCK_SEQPACKET, "/tmp/bsc_mncc");
222 if (rc < 0) {
223 talloc_free(state);
224 return rc;
225 }
226
227 bfd->when = BSC_FD_READ;
228 bfd->cb = mncc_sock_accept;
229 bfd->data = state;
230
231 rc = bsc_register_fd(bfd);
232 if (rc < 0) {
233 fprintf(stderr, "Failed to register the bfd.\n");
234 close(bfd->fd);
235 talloc_free(state);
236 return rc;
237 }
238
239 g_state = state;
240
241 return 0;
242}
243
244/* FIXME: move this to libosmocore */
245int osmo_unixsock_listen(struct bsc_fd *bfd, int type, const char *path)
246{
247 struct sockaddr_un local;
248 unsigned int namelen;
249 int rc;
250
251 bfd->fd = socket(AF_UNIX, type, 0);
252
253 if (bfd->fd < 0) {
254 fprintf(stderr, "Failed to create Unix Domain Socket.\n");
255 return -1;
256 }
257
258 local.sun_family = AF_UNIX;
259 strncpy(local.sun_path, path, sizeof(local.sun_path));
260 local.sun_path[sizeof(local.sun_path) - 1] = '\0';
261 unlink(local.sun_path);
262
263 /* we use the same magic that X11 uses in Xtranssock.c for
264 * calculating the proper length of the sockaddr */
265#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
266 local.sun_len = strlen(local.sun_path);
267#endif
268#if defined(BSD44SOCKETS) || defined(SUN_LEN)
269 namelen = SUN_LEN(&local);
270#else
271 namelen = strlen(local.sun_path) +
272 offsetof(struct sockaddr_un, sun_path);
273#endif
274
275 rc = bind(bfd->fd, (struct sockaddr *) &local, namelen);
276 if (rc != 0) {
277 fprintf(stderr, "Failed to bind the unix domain socket. '%s'\n",
278 local.sun_path);
279 return -1;
280 }
281
282 if (listen(bfd->fd, 0) != 0) {
283 fprintf(stderr, "Failed to listen.\n");
284 return -1;
285 }
286
287 return 0;
288}