blob: bcf8ec6987c216d18ce543d6c587aaadff983052 [file] [log] [blame]
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +08001/* Implementation of the C7 UDP link */
2/*
3 * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org>
4 * (C) 2010 by On-Waves
5 * All Rights Reserved
6 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +01007 * This program is free software: you can redistribute it and/or modify
8 * 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 Freyther97f66e22010-07-28 03:32:52 +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
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +010015 * GNU Affero General Public License for more details.
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080016 *
Holger Hans Peter Freytherde56c222011-01-16 17:45:14 +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 Freyther97f66e22010-07-28 03:32:52 +080019 *
20 */
21
22#include <bsc_data.h>
23#include <udp_input.h>
24#include <mtp_data.h>
25#include <mtp_pcap.h>
26#include <snmp_mtp.h>
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080027#include <cellmgr_debug.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080028
Holger Hans Peter Freythercbf7d182010-07-31 05:25:35 +080029#include <osmocore/talloc.h>
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080030
31#include <sys/socket.h>
32#include <netinet/in.h>
33#include <arpa/inet.h>
34
35#include <string.h>
36#include <unistd.h>
37
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010038static struct mtp_udp_link *find_link(struct mtp_udp_data *data, uint16_t link_index)
39{
40 struct mtp_udp_link *lnk;
41
42 llist_for_each_entry(lnk, &data->links, entry)
43 if (lnk->link_index == link_index)
44 return lnk;
45
46 return NULL;
47}
48
49
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080050static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg)
51{
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010052 struct mtp_udp_data *data;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +010053 struct mtp_udp_link *link;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080054 int rc;
55
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010056 data = fd->data;
57 link = find_link(data, msg->cb[0]);
58 if (!link) {
59 LOGP(DINP, LOGL_ERROR, "Failed to find link with %lu\n", msg->cb[0]);
60 return -1;
61 }
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080062
63 LOGP(DINP, LOGL_DEBUG, "Sending MSU: %s\n", hexdump(msg->data, msg->len));
Holger Hans Peter Freyther36260e92011-01-22 17:37:56 +010064 mtp_handle_pcap(&link->base, NET_OUT, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080065
66 /* the assumption is we have connected the socket to the remote */
67 rc = sendto(fd->fd, msg->data, msg->len, 0,
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +010068 (struct sockaddr *) &link->remote, sizeof(link->remote));
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080069 if (rc != msg->len) {
70 LOGP(DINP, LOGL_ERROR, "Failed to write msg to socket: %d\n", rc);
71 return -1;
72 }
73
74 return 0;
75}
76
77static int udp_read_cb(struct bsc_fd *fd)
78{
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010079 struct mtp_udp_data *data;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +010080 struct mtp_link *link;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080081 struct udp_data_hdr *hdr;
82 struct msgb *msg;
83 int rc;
84 unsigned int length;
85
86 msg = msgb_alloc_headroom(4096, 128, "UDP datagram");
87 if (!msg) {
88 LOGP(DINP, LOGL_ERROR, "Failed to allocate memory.\n");
89 return -1;
90 }
91
92
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010093 data = (struct mtp_udp_data *) fd->data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080094 rc = read(fd->fd, msg->data, 2096);
95 if (rc < sizeof(*hdr)) {
96 LOGP(DINP, LOGL_ERROR, "Failed to read at least size of the header: %d\n", rc);
97 rc = -1;
98 goto exit;
99 }
100
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100101 hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
102 link = (struct mtp_link *) find_link(data, ntohs(hdr->data_link_index));
103
104 if (!link) {
105 LOGP(DINP, LOGL_ERROR, "No link registered for %d\n",
106 ntohs(hdr->data_link_index));
107 goto exit;
108 }
109
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100110 if (link->blocked) {
111 LOGP(DINP, LOGL_ERROR, "The link is blocked.\n");
112 rc = 0;
113 goto exit;
114 }
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100115
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800116 if (hdr->data_type == UDP_DATA_RETR_COMPL || hdr->data_type == UDP_DATA_RETR_IMPOS) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100117 LOGP(DINP, LOGL_ERROR, "Link retrieval done on %s/%d.\n",
118 link->set->name, link->link_no);
Holger Hans Peter Freytherfa8cf2d2011-01-20 16:51:34 +0100119 mtp_link_failure(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800120 goto exit;
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100121 } else if (hdr->data_type == UDP_DATA_LINK_UP) {
122 LOGP(DINP, LOGL_NOTICE, "Link of %s/%d is up.\n",
123 link->set->name, link->link_no);
124 mtp_link_up(link);
125 goto exit;
126 } else if (hdr->data_type == UDP_DATA_LINK_DOWN) {
127 LOGP(DINP, LOGL_NOTICE, "Link of %s/%d is down.\n",
128 link->set->name, link->link_no);
129 mtp_link_failure(link);
130 goto exit;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800131 } else if (hdr->data_type > UDP_DATA_MSU_PRIO_3) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100132 LOGP(DINP, LOGL_ERROR, "Link failue on %s/%d.\n",
133 link->set->name, link->link_no);
Holger Hans Peter Freytherfa8cf2d2011-01-20 16:51:34 +0100134 mtp_link_failure(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800135 goto exit;
136 }
137
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100138 /* throw away data as the link is down */
139 if (link->set->available == 0) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100140 LOGP(DINP, LOGL_ERROR, "Link %s/%d is down. Not forwarding.\n",
141 link->set->name, link->link_no);
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100142 rc = 0;
143 goto exit;
144 }
145
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800146 length = ntohl(hdr->data_length);
147 if (length + sizeof(*hdr) > (unsigned int) rc) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100148 LOGP(DINP, LOGL_ERROR,
149 "The MSU payload does not fit: %u + %u > %d on %s/%d.\n",
150 length, sizeof(*hdr), rc, link->set->name, link->link_no);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800151 rc = -1;
152 goto exit;
153 }
154
155 msg->l2h = msgb_put(msg, length);
156
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100157 LOGP(DINP, LOGL_DEBUG, "MSU data on: %s/%d data %s.\n",
158 link->set->name, link->link_no, hexdump(msg->data, msg->len));
Holger Hans Peter Freyther36260e92011-01-22 17:37:56 +0100159 mtp_handle_pcap(link, NET_IN, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freytherbee2ed12011-01-18 13:29:42 +0100160 mtp_link_set_data(link, msg);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800161
162exit:
163 msgb_free(msg);
164 return rc;
165}
166
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100167static int udp_link_dummy(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800168{
169 /* nothing todo */
170 return 0;
171}
172
173static void do_start(void *_data)
174{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100175 struct mtp_udp_link *link = (struct mtp_udp_link *) _data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800176
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100177 snmp_mtp_activate(link->session, link->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800178}
179
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100180static int udp_link_reset(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800181{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100182 struct mtp_udp_link *ulnk;
183
184 ulnk = (struct mtp_udp_link *) link;
185
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100186 snmp_mtp_deactivate(ulnk->session, ulnk->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800187 return 0;
188}
189
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100190static int udp_link_shutdown(struct mtp_link *link)
191{
192 return udp_link_reset(link);
193}
194
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100195static int udp_link_write(struct mtp_link *link, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800196{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100197 struct mtp_udp_link *ulnk;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800198 struct udp_data_hdr *hdr;
199
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100200 ulnk = (struct mtp_udp_link *) link;
201
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800202 hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr));
203 hdr->format_type = UDP_FORMAT_SIMPLE_UDP;
204 hdr->data_type = UDP_DATA_MSU_PRIO_0;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100205 hdr->data_link_index = htons(ulnk->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800206 hdr->user_context = 0;
207 hdr->data_length = htonl(msgb_l2len(msg));
208
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100209 msg->cb[0] = ulnk->link_index;
210
211 if (write_queue_enqueue(&ulnk->data->write_queue, msg) != 0) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100212 LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg on %s/%d.\n",
213 link->set->name, link->link_no);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800214 msgb_free(msg);
215 return -1;
216 }
217
218 return 0;
219}
220
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100221static int udp_link_start(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800222{
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800223 do_start(link);
224 return 0;
225}
226
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100227int link_udp_init(struct mtp_udp_link *link, char *remote, int port)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800228{
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100229 /* setup SNMP first, it is blocking */
230 link->session = snmp_mtp_session_create(remote);
231 if (!link->session)
232 return -1;
233 link->session->data = link;
234
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800235 /* function table */
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100236 link->base.shutdown = udp_link_shutdown;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100237 link->base.clear_queue = udp_link_dummy;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800238
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100239 link->base.reset = udp_link_reset;
240 link->base.start = udp_link_start;
241 link->base.write = udp_link_write;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800242
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100243 /* prepare the remote */
244 memset(&link->remote, 0, sizeof(link->remote));
245 link->remote.sin_family = AF_INET;
246 link->remote.sin_port = htons(port);
247 inet_aton(remote, &link->remote.sin_addr);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800248
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100249 /* add it to the list of udp connections */
250 llist_add(&link->entry, &link->data->links);
251 return 0;
252}
253
Holger Hans Peter Freyther083a30b2011-01-21 12:57:12 +0100254static void snmp_poll(void *_data)
255{
256 struct mtp_udp_data *data = _data;
257 snmp_mtp_poll();
258 bsc_schedule_timer(&data->snmp_poll, 0, 5000);
259}
260
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100261int link_global_init(struct mtp_udp_data *data, int src_port)
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100262{
263 struct sockaddr_in addr;
264 int fd;
265 int on;
266
267 INIT_LLIST_HEAD(&data->links);
268 write_queue_init(&data->write_queue, 100);
269
270 /* socket creation */
271 data->write_queue.bfd.data = data;
272 data->write_queue.bfd.when = BSC_FD_READ;
273 data->write_queue.read_cb = udp_read_cb;
274 data->write_queue.write_cb = udp_write_cb;
275
276 data->write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800277 if (fd < 0) {
278 LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n");
279 return -1;
280 }
281
282 on = 1;
283 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
284
285 memset(&addr, 0, sizeof(addr));
286 addr.sin_family = AF_INET;
287 addr.sin_port = htons(src_port);
288 addr.sin_addr.s_addr = INADDR_ANY;
289
290 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
291 perror("Failed to bind UDP socket");
292 close(fd);
293 return -1;
294 }
295
296 /* now connect the socket to the remote */
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100297 if (bsc_register_fd(&data->write_queue.bfd) != 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800298 LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n");
299 close(fd);
300 return -1;
301 }
302
Holger Hans Peter Freyther083a30b2011-01-21 12:57:12 +0100303 data->snmp_poll.data = data;
304 data->snmp_poll.cb = snmp_poll;
305 snmp_poll(data);
306
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800307 return 0;
308}
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100309
310void snmp_mtp_callback(struct snmp_mtp_session *session,
311 int area, int res, int link_id)
312{
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100313 struct mtp_udp_link *ulink;
314 struct mtp_link *link;
315
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100316 ulink = session->data;
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100317 if (!ulink)
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100318 return LOGP(DINP, LOGL_ERROR, "Failed to find link_id %d\n", link_id);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100319
320 link = &ulink->base;
321
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100322 if (res == SNMP_STATUS_TIMEOUT && !link->blocked) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100323 LOGP(DINP, LOGL_ERROR, "Failed to restart link: %s/%d\n",
324 link->set->name, link->link_no);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100325 udp_link_reset(link);
326 return;
327 }
328
329 switch (area) {
330 case SNMP_LINK_UP:
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100331 break;
332 case SNMP_LINK_DOWN:
333 mtp_link_down(link);
334
335 /*
336 * restart the link in 90 seconds...
337 * to force a timeout on the BSC
338 */
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100339 if (!link->blocked) {
340 link->link_activate.cb = do_start;
341 link->link_activate.data = link;
342 bsc_schedule_timer(&link->link_activate, ulink->reset_timeout, 0);
343 LOGP(DINP, LOGL_NOTICE,
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100344 "Will bring up link %s/%d in %d seconds.\n",
345 link->set->name, link->link_no, ulink->reset_timeout);
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100346 }
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100347 break;
348 default:
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100349 LOGP(DINP, LOGL_ERROR, "Unknown event %d on %s/%d.\n",
350 area, link->set->name, link->link_no);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100351 }
352}