blob: 04dcec8e16a5469aeb5a609d271bfd93d3aa8450 [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 Freyther3472eb52011-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 Freyther3472eb52011-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 Freyther3472eb52011-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 Freyther0e2f9112011-01-17 11:54:39 +010064 if (link->base.pcap_fd >= 0)
65 mtp_pcap_write_msu(link->base.pcap_fd, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080066
67 /* the assumption is we have connected the socket to the remote */
68 rc = sendto(fd->fd, msg->data, msg->len, 0,
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +010069 (struct sockaddr *) &link->remote, sizeof(link->remote));
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080070 if (rc != msg->len) {
71 LOGP(DINP, LOGL_ERROR, "Failed to write msg to socket: %d\n", rc);
72 return -1;
73 }
74
75 return 0;
76}
77
78static int udp_read_cb(struct bsc_fd *fd)
79{
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +010080 struct mtp_udp_data *data;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +010081 struct mtp_link *link;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080082 struct udp_data_hdr *hdr;
83 struct msgb *msg;
84 int rc;
85 unsigned int length;
86
87 msg = msgb_alloc_headroom(4096, 128, "UDP datagram");
88 if (!msg) {
89 LOGP(DINP, LOGL_ERROR, "Failed to allocate memory.\n");
90 return -1;
91 }
92
93
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +010094 data = (struct mtp_udp_data *) fd->data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080095 rc = read(fd->fd, msg->data, 2096);
96 if (rc < sizeof(*hdr)) {
97 LOGP(DINP, LOGL_ERROR, "Failed to read at least size of the header: %d\n", rc);
98 rc = -1;
99 goto exit;
100 }
101
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100102 hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
103 link = (struct mtp_link *) find_link(data, ntohs(hdr->data_link_index));
104
105 if (!link) {
106 LOGP(DINP, LOGL_ERROR, "No link registered for %d\n",
107 ntohs(hdr->data_link_index));
108 goto exit;
109 }
110
111
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800112 /* throw away data as the link is down */
Holger Hans Peter Freyther2d845fc2011-01-20 15:42:13 +0100113 if (link->set->available == 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800114 LOGP(DINP, LOGL_ERROR, "The link is down. Not forwarding.\n");
115 rc = 0;
116 goto exit;
117 }
118
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800119 if (hdr->data_type == UDP_DATA_RETR_COMPL || hdr->data_type == UDP_DATA_RETR_IMPOS) {
120 LOGP(DINP, LOGL_ERROR, "Link retrieval done. Restarting the link.\n");
Holger Hans Peter Freytherfa8cf2d2011-01-20 16:51:34 +0100121 mtp_link_failure(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800122 goto exit;
123 } else if (hdr->data_type > UDP_DATA_MSU_PRIO_3) {
124 LOGP(DINP, LOGL_ERROR, "Link failure. retrieved message.\n");
Holger Hans Peter Freytherfa8cf2d2011-01-20 16:51:34 +0100125 mtp_link_failure(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800126 goto exit;
127 }
128
129 length = ntohl(hdr->data_length);
130 if (length + sizeof(*hdr) > (unsigned int) rc) {
131 LOGP(DINP, LOGL_ERROR, "The MSU payload does not fit: %u + %u > %d \n",
132 length, sizeof(*hdr), rc);
133 rc = -1;
134 goto exit;
135 }
136
137 msg->l2h = msgb_put(msg, length);
138
139 LOGP(DINP, LOGL_DEBUG, "MSU data on: %p data %s.\n", link, hexdump(msg->data, msg->len));
140 if (link->pcap_fd >= 0)
141 mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freytherbee2ed12011-01-18 13:29:42 +0100142 mtp_link_set_data(link, msg);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800143
144exit:
145 msgb_free(msg);
146 return rc;
147}
148
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100149static int udp_link_dummy(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800150{
151 /* nothing todo */
152 return 0;
153}
154
155static void do_start(void *_data)
156{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100157 struct mtp_udp_link *link = (struct mtp_udp_link *) _data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800158
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100159 snmp_mtp_activate(link->data->session, link->link_index);
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100160 mtp_link_up(&link->base);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800161}
162
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100163static int udp_link_reset(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800164{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100165 struct mtp_udp_link *ulnk;
166
167 ulnk = (struct mtp_udp_link *) link;
168
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800169 LOGP(DINP, LOGL_NOTICE, "Will restart SLTM transmission in %d seconds.\n",
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100170 ulnk->reset_timeout);
171
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100172 snmp_mtp_deactivate(ulnk->data->session, ulnk->link_index);
Holger Hans Peter Freytherc8405692011-01-02 20:24:08 +0100173 mtp_link_down(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800174
175 /* restart the link in 90 seconds... to force a timeout on the BSC */
176 link->link_activate.cb = do_start;
177 link->link_activate.data = link;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100178 bsc_schedule_timer(&link->link_activate, ulnk->reset_timeout, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800179 return 0;
180}
181
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100182static int udp_link_write(struct mtp_link *link, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800183{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100184 struct mtp_udp_link *ulnk;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800185 struct udp_data_hdr *hdr;
186
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100187 ulnk = (struct mtp_udp_link *) link;
188
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800189 hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr));
190 hdr->format_type = UDP_FORMAT_SIMPLE_UDP;
191 hdr->data_type = UDP_DATA_MSU_PRIO_0;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100192 hdr->data_link_index = htons(ulnk->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800193 hdr->user_context = 0;
194 hdr->data_length = htonl(msgb_l2len(msg));
195
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100196 msg->cb[0] = ulnk->link_index;
197
198 if (write_queue_enqueue(&ulnk->data->write_queue, msg) != 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800199 LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg.\n");
200 msgb_free(msg);
201 return -1;
202 }
203
204 return 0;
205}
206
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100207static int udp_link_start(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800208{
209 LOGP(DINP, LOGL_NOTICE, "UDP input is ready.\n");
210 do_start(link);
211 return 0;
212}
213
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100214int link_udp_init(struct mtp_udp_link *link, const char *remote, int port)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800215{
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800216 /* function table */
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100217 link->base.shutdown = udp_link_dummy;
218 link->base.clear_queue = udp_link_dummy;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800219
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100220 link->base.reset = udp_link_reset;
221 link->base.start = udp_link_start;
222 link->base.write = udp_link_write;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800223
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100224 /* prepare the remote */
225 memset(&link->remote, 0, sizeof(link->remote));
226 link->remote.sin_family = AF_INET;
227 link->remote.sin_port = htons(port);
228 inet_aton(remote, &link->remote.sin_addr);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800229
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100230 /* add it to the list of udp connections */
231 llist_add(&link->entry, &link->data->links);
232 return 0;
233}
234
235int link_global_init(struct mtp_udp_data *data, int src_port)
236{
237 struct sockaddr_in addr;
238 int fd;
239 int on;
240
241 INIT_LLIST_HEAD(&data->links);
242 write_queue_init(&data->write_queue, 100);
243
244 /* socket creation */
245 data->write_queue.bfd.data = data;
246 data->write_queue.bfd.when = BSC_FD_READ;
247 data->write_queue.read_cb = udp_read_cb;
248 data->write_queue.write_cb = udp_write_cb;
249
250 data->write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800251 if (fd < 0) {
252 LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n");
253 return -1;
254 }
255
256 on = 1;
257 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
258
259 memset(&addr, 0, sizeof(addr));
260 addr.sin_family = AF_INET;
261 addr.sin_port = htons(src_port);
262 addr.sin_addr.s_addr = INADDR_ANY;
263
264 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
265 perror("Failed to bind UDP socket");
266 close(fd);
267 return -1;
268 }
269
270 /* now connect the socket to the remote */
Holger Hans Peter Freyther3472eb52011-01-20 21:11:13 +0100271 if (bsc_register_fd(&data->write_queue.bfd) != 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800272 LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n");
273 close(fd);
274 return -1;
275 }
276
277 return 0;
278}