blob: e05bd175663e0fa9c5afcdf9adfcd296aeaeddbc [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
38static int udp_write_cb(struct bsc_fd *fd, struct msgb *msg)
39{
40 struct link_data *link;
41 int rc;
42
43 link = (struct link_data *) fd->data;
44
45 LOGP(DINP, LOGL_DEBUG, "Sending MSU: %s\n", hexdump(msg->data, msg->len));
46 if (link->pcap_fd >= 0)
47 mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg));
48
49 /* the assumption is we have connected the socket to the remote */
50 rc = sendto(fd->fd, msg->data, msg->len, 0,
51 (struct sockaddr *) &link->udp.remote, sizeof(link->udp.remote));
52 if (rc != msg->len) {
53 LOGP(DINP, LOGL_ERROR, "Failed to write msg to socket: %d\n", rc);
54 return -1;
55 }
56
57 return 0;
58}
59
60static int udp_read_cb(struct bsc_fd *fd)
61{
62 struct link_data *link;
63 struct udp_data_hdr *hdr;
64 struct msgb *msg;
65 int rc;
66 unsigned int length;
67
68 msg = msgb_alloc_headroom(4096, 128, "UDP datagram");
69 if (!msg) {
70 LOGP(DINP, LOGL_ERROR, "Failed to allocate memory.\n");
71 return -1;
72 }
73
74
75 link = (struct link_data *) fd->data;
76 rc = read(fd->fd, msg->data, 2096);
77 if (rc < sizeof(*hdr)) {
78 LOGP(DINP, LOGL_ERROR, "Failed to read at least size of the header: %d\n", rc);
79 rc = -1;
80 goto exit;
81 }
82
83 /* throw away data as the link is down */
84 if (link->the_link->available == 0) {
85 LOGP(DINP, LOGL_ERROR, "The link is down. Not forwarding.\n");
86 rc = 0;
87 goto exit;
88 }
89
90 hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
91
92 if (hdr->data_type == UDP_DATA_RETR_COMPL || hdr->data_type == UDP_DATA_RETR_IMPOS) {
93 LOGP(DINP, LOGL_ERROR, "Link retrieval done. Restarting the link.\n");
Holger Hans Peter Freytherc8405692011-01-02 20:24:08 +010094 mtp_link_down(link);
95 mtp_link_up(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080096 goto exit;
97 } else if (hdr->data_type > UDP_DATA_MSU_PRIO_3) {
98 LOGP(DINP, LOGL_ERROR, "Link failure. retrieved message.\n");
Holger Hans Peter Freytherc8405692011-01-02 20:24:08 +010099 mtp_link_down(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800100 goto exit;
101 }
102
103 length = ntohl(hdr->data_length);
104 if (length + sizeof(*hdr) > (unsigned int) rc) {
105 LOGP(DINP, LOGL_ERROR, "The MSU payload does not fit: %u + %u > %d \n",
106 length, sizeof(*hdr), rc);
107 rc = -1;
108 goto exit;
109 }
110
111 msg->l2h = msgb_put(msg, length);
112
113 LOGP(DINP, LOGL_DEBUG, "MSU data on: %p data %s.\n", link, hexdump(msg->data, msg->len));
114 if (link->pcap_fd >= 0)
115 mtp_pcap_write_msu(link->pcap_fd, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freyther569f1e12011-01-02 18:47:49 +0100116 mtp_link_set_data(link->the_link, msg);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800117
118exit:
119 msgb_free(msg);
120 return rc;
121}
122
123static int udp_link_dummy(struct link_data *link)
124{
125 /* nothing todo */
126 return 0;
127}
128
129static void do_start(void *_data)
130{
131 struct link_data *link = (struct link_data *) _data;
132
Holger Hans Peter Freytherb6edf972011-01-02 16:41:11 +0100133 snmp_mtp_activate(link->udp.session, link->udp.link_index);
Holger Hans Peter Freytherc8405692011-01-02 20:24:08 +0100134 mtp_link_up(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800135}
136
137static int udp_link_reset(struct link_data *link)
138{
139 LOGP(DINP, LOGL_NOTICE, "Will restart SLTM transmission in %d seconds.\n",
140 link->udp.reset_timeout);
Holger Hans Peter Freytherb6edf972011-01-02 16:41:11 +0100141 snmp_mtp_deactivate(link->udp.session, link->udp.link_index);
Holger Hans Peter Freytherc8405692011-01-02 20:24:08 +0100142 mtp_link_down(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800143
144 /* restart the link in 90 seconds... to force a timeout on the BSC */
145 link->link_activate.cb = do_start;
146 link->link_activate.data = link;
147 bsc_schedule_timer(&link->link_activate, link->udp.reset_timeout, 0);
148 return 0;
149}
150
151static int udp_link_write(struct link_data *link, struct msgb *msg)
152{
153 struct udp_data_hdr *hdr;
154
155 hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr));
156 hdr->format_type = UDP_FORMAT_SIMPLE_UDP;
157 hdr->data_type = UDP_DATA_MSU_PRIO_0;
Holger Hans Peter Freytherb6edf972011-01-02 16:41:11 +0100158 hdr->data_link_index = htons(link->udp.link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800159 hdr->user_context = 0;
160 hdr->data_length = htonl(msgb_l2len(msg));
161
162 if (write_queue_enqueue(&link->udp.write_queue, msg) != 0) {
163 LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg.\n");
164 msgb_free(msg);
165 return -1;
166 }
167
168 return 0;
169}
170
171static int udp_link_start(struct link_data *link)
172{
173 LOGP(DINP, LOGL_NOTICE, "UDP input is ready.\n");
174 do_start(link);
175 return 0;
176}
177
178int link_udp_init(struct link_data *link, int src_port, const char *remote, int remote_port)
179{
180 struct sockaddr_in addr;
181 int fd;
182 int on;
183
184 write_queue_init(&link->udp.write_queue, 100);
185
186 /* function table */
187 link->shutdown = udp_link_dummy;
188 link->clear_queue = udp_link_dummy;
189
190 link->reset = udp_link_reset;
191 link->start = udp_link_start;
192 link->write = udp_link_write;
193
194 /* socket creation */
195 link->udp.write_queue.bfd.data = link;
196 link->udp.write_queue.bfd.when = BSC_FD_READ;
197 link->udp.write_queue.read_cb = udp_read_cb;
198 link->udp.write_queue.write_cb = udp_write_cb;
199
200 link->udp.write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0);
201 if (fd < 0) {
202 LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n");
203 return -1;
204 }
205
206 on = 1;
207 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
208
209 memset(&addr, 0, sizeof(addr));
210 addr.sin_family = AF_INET;
211 addr.sin_port = htons(src_port);
212 addr.sin_addr.s_addr = INADDR_ANY;
213
214 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
215 perror("Failed to bind UDP socket");
216 close(fd);
217 return -1;
218 }
219
220 /* now connect the socket to the remote */
221 memset(&link->udp.remote, 0, sizeof(link->udp.remote));
222 link->udp.remote.sin_family = AF_INET;
223 link->udp.remote.sin_port = htons(remote_port);
224 inet_aton(remote, &link->udp.remote.sin_addr);
225
226 if (bsc_register_fd(&link->udp.write_queue.bfd) != 0) {
227 LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n");
228 close(fd);
229 return -1;
230 }
231
232 return 0;
233}