blob: bc9cf2fc8056670b84dcb696afaf9404844fda2b [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
Harald Welteff397ed2011-05-08 10:29:23 +020029#include <osmocom/core/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
Harald Welteff397ed2011-05-08 10:29:23 +020050static int udp_write_cb(struct osmo_fd *fd, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080051{
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
Harald Welteff397ed2011-05-08 10:29:23 +020063 LOGP(DINP, LOGL_DEBUG, "Sending MSU: %s\n", osmo_hexdump(msg->data, msg->len));
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +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
Harald Welteff397ed2011-05-08 10:29:23 +020077static int udp_read_cb(struct osmo_fd *fd)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +080078{
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +010079 struct mtp_udp_data *data;
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +010080 struct mtp_udp_link *ulnk;
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 Freytherf38114e2011-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 Freytherf38114e2011-01-20 21:11:13 +0100102 hdr = (struct udp_data_hdr *) msgb_put(msg, sizeof(*hdr));
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100103 ulnk = find_link(data, ntohs(hdr->data_link_index));
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100104
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100105 if (!ulnk) {
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100106 LOGP(DINP, LOGL_ERROR, "No link registered for %d\n",
107 ntohs(hdr->data_link_index));
108 goto exit;
109 }
110
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100111 link = ulnk->base;
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100112 if (link->blocked) {
113 LOGP(DINP, LOGL_ERROR, "The link is blocked.\n");
114 rc = 0;
115 goto exit;
116 }
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100117
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800118 if (hdr->data_type == UDP_DATA_RETR_COMPL || hdr->data_type == UDP_DATA_RETR_IMPOS) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100119 LOGP(DINP, LOGL_ERROR, "Link retrieval done on %d/%s of %d/%s.\n",
120 link->nr, link->name, link->set->nr, link->set->name);
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;
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100123 } else if (hdr->data_type == UDP_DATA_LINK_UP) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100124 LOGP(DINP, LOGL_NOTICE, "Link of %d/%s of %d/%s is up.\n",
125 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100126 mtp_link_up(link);
127 goto exit;
128 } else if (hdr->data_type == UDP_DATA_LINK_DOWN) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100129 LOGP(DINP, LOGL_NOTICE, "Link of %d/%s of %d/%s is down.\n",
130 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100131 mtp_link_failure(link);
132 goto exit;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800133 } else if (hdr->data_type > UDP_DATA_MSU_PRIO_3) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100134 LOGP(DINP, LOGL_ERROR, "Link failue on %d/%s of %d/%s.\n",
135 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freytherfa8cf2d2011-01-20 16:51:34 +0100136 mtp_link_failure(link);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800137 goto exit;
138 }
139
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100140 /* throw away data as the link is down */
141 if (link->set->available == 0) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100142 LOGP(DINP, LOGL_ERROR, "Link %d/%s of %d/%s is down. Not forwarding.\n",
143 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freyther86a2a532011-01-28 17:47:25 +0100144 rc = 0;
145 goto exit;
146 }
147
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800148 length = ntohl(hdr->data_length);
149 if (length + sizeof(*hdr) > (unsigned int) rc) {
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100150 LOGP(DINP, LOGL_ERROR,
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100151 "The MSU payload does not fit: %u + %u > %d on link %d/%s of %d/%s.\n",
152 length, sizeof(*hdr), rc,
153 link->nr, link->name, link->set->nr, link->set->name);
154 rc = 0;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800155 rc = -1;
156 goto exit;
157 }
158
159 msg->l2h = msgb_put(msg, length);
160
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100161 LOGP(DINP, LOGL_DEBUG, "MSU data on link %d/%s of %d/%s data %s.\n",
162 link->nr, link->name, link->set->nr, link->set->name,
Harald Welteff397ed2011-05-08 10:29:23 +0200163 osmo_hexdump(msg->data, msg->len));
Holger Hans Peter Freyther36260e92011-01-22 17:37:56 +0100164 mtp_handle_pcap(link, NET_IN, msg->l2h, msgb_l2len(msg));
Holger Hans Peter Freytherbee2ed12011-01-18 13:29:42 +0100165 mtp_link_set_data(link, msg);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800166
167exit:
168 msgb_free(msg);
169 return rc;
170}
171
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100172static int udp_link_dummy(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800173{
174 /* nothing todo */
175 return 0;
176}
177
178static void do_start(void *_data)
179{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100180 struct mtp_udp_link *link = (struct mtp_udp_link *) _data;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800181
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100182 snmp_mtp_activate(link->session, link->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800183}
184
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100185static int udp_link_reset(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800186{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100187 struct mtp_udp_link *ulnk;
188
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100189 ulnk = (struct mtp_udp_link *) link->data;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100190
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100191 snmp_mtp_deactivate(ulnk->session, ulnk->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800192 return 0;
193}
194
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100195static int udp_link_shutdown(struct mtp_link *link)
196{
197 return udp_link_reset(link);
198}
199
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100200static int udp_link_write(struct mtp_link *link, struct msgb *msg)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800201{
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100202 struct mtp_udp_link *ulnk;
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800203 struct udp_data_hdr *hdr;
204
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100205 ulnk = (struct mtp_udp_link *) link->data;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100206
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800207 hdr = (struct udp_data_hdr *) msgb_push(msg, sizeof(*hdr));
208 hdr->format_type = UDP_FORMAT_SIMPLE_UDP;
209 hdr->data_type = UDP_DATA_MSU_PRIO_0;
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100210 hdr->data_link_index = htons(ulnk->link_index);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800211 hdr->user_context = 0;
212 hdr->data_length = htonl(msgb_l2len(msg));
213
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100214 msg->cb[0] = ulnk->link_index;
215
Harald Welteff397ed2011-05-08 10:29:23 +0200216 if (osmo_wqueue_enqueue(&ulnk->data->write_queue, msg) != 0) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100217 LOGP(DINP, LOGL_ERROR, "Failed to enqueue msg on link %d/%s of %d/%s.\n",
218 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800219 msgb_free(msg);
220 return -1;
221 }
222
223 return 0;
224}
225
Holger Hans Peter Freyther0e2f9112011-01-17 11:54:39 +0100226static int udp_link_start(struct mtp_link *link)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800227{
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800228 do_start(link);
229 return 0;
230}
231
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100232int link_udp_init(struct mtp_udp_link *link, char *remote, int port)
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800233{
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100234 /* prepare the remote */
235 memset(&link->remote, 0, sizeof(link->remote));
236 link->remote.sin_family = AF_INET;
237 link->remote.sin_port = htons(port);
238 inet_aton(remote, &link->remote.sin_addr);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800239
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100240 return 0;
241}
242
Holger Hans Peter Freyther083a30b2011-01-21 12:57:12 +0100243static void snmp_poll(void *_data)
244{
245 struct mtp_udp_data *data = _data;
246 snmp_mtp_poll();
Harald Welteff397ed2011-05-08 10:29:23 +0200247 osmo_timer_schedule(&data->snmp_poll, 0, 5000);
Holger Hans Peter Freyther083a30b2011-01-21 12:57:12 +0100248}
249
Holger Hans Peter Freyther71760302011-02-22 20:57:08 +0100250int link_global_init(struct mtp_udp_data *data)
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100251{
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100252 INIT_LLIST_HEAD(&data->links);
Harald Welteff397ed2011-05-08 10:29:23 +0200253 osmo_wqueue_init(&data->write_queue, 100);
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100254
255 /* socket creation */
256 data->write_queue.bfd.data = data;
257 data->write_queue.bfd.when = BSC_FD_READ;
258 data->write_queue.read_cb = udp_read_cb;
259 data->write_queue.write_cb = udp_write_cb;
260
Holger Hans Peter Freyther71760302011-02-22 20:57:08 +0100261 return 0;
262}
263
264int link_global_bind(struct mtp_udp_data *data, int src_port)
265{
266 struct sockaddr_in addr;
267 int fd;
268 int on;
269
Holger Hans Peter Freytherf38114e2011-01-20 21:11:13 +0100270 data->write_queue.bfd.fd = fd = socket(AF_INET, SOCK_DGRAM, 0);
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800271 if (fd < 0) {
272 LOGP(DINP, LOGL_ERROR, "Failed to create UDP socket.\n");
273 return -1;
274 }
275
276 on = 1;
277 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
278
279 memset(&addr, 0, sizeof(addr));
280 addr.sin_family = AF_INET;
281 addr.sin_port = htons(src_port);
282 addr.sin_addr.s_addr = INADDR_ANY;
283
284 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
285 perror("Failed to bind UDP socket");
286 close(fd);
287 return -1;
288 }
289
290 /* now connect the socket to the remote */
Harald Welteff397ed2011-05-08 10:29:23 +0200291 if (osmo_fd_register(&data->write_queue.bfd) != 0) {
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800292 LOGP(DINP, LOGL_ERROR, "Failed to register BFD.\n");
293 close(fd);
294 return -1;
295 }
296
Holger Hans Peter Freyther083a30b2011-01-21 12:57:12 +0100297 data->snmp_poll.data = data;
298 data->snmp_poll.cb = snmp_poll;
299 snmp_poll(data);
300
Holger Hans Peter Freyther97f66e22010-07-28 03:32:52 +0800301 return 0;
302}
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100303
304void snmp_mtp_callback(struct snmp_mtp_session *session,
305 int area, int res, int link_id)
306{
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100307 struct mtp_udp_link *ulink;
308 struct mtp_link *link;
309
Holger Hans Peter Freyther3a1c0af2011-01-24 20:21:11 +0100310 ulink = session->data;
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100311 if (!ulink)
Holger Hans Peter Freytherd3f412b2011-01-28 18:52:16 +0100312 return LOGP(DINP, LOGL_ERROR, "Failed to find link_id %d\n", link_id);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100313
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100314 link = ulink->base;
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100315
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100316 if (res == SNMP_STATUS_TIMEOUT && !link->blocked) {
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100317 LOGP(DINP, LOGL_ERROR,
318 "Failed to restart link %d/%s of linkset %d/%s\n",
319 link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100320 udp_link_reset(link);
321 return;
322 }
323
324 switch (area) {
325 case SNMP_LINK_UP:
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100326 break;
327 case SNMP_LINK_DOWN:
328 mtp_link_down(link);
329
330 /*
331 * restart the link in 90 seconds...
332 * to force a timeout on the BSC
333 */
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100334 if (!link->blocked) {
335 link->link_activate.cb = do_start;
Holger Hans Peter Freyther6c0b2e52011-02-17 02:18:38 +0100336 link->link_activate.data = ulink;
Harald Welteff397ed2011-05-08 10:29:23 +0200337 osmo_timer_schedule(&link->link_activate, ulink->reset_timeout, 0);
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100338 LOGP(DINP, LOGL_NOTICE,
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100339 "Will bring up link %d/%s of linkset %d/%s in %d seconds.\n",
340 link->nr, link->name,
341 link->set->nr, link->set->name,
342 ulink->reset_timeout);
Holger Hans Peter Freytherea5ce232011-01-23 23:31:26 +0100343 }
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100344 break;
345 default:
Holger Hans Peter Freyther5a34c7f2011-02-17 03:23:42 +0100346 LOGP(DINP, LOGL_ERROR,
347 "Unknown event %d on %d/%s of linkset %d/%s.\n",
348 area, link->nr, link->name, link->set->nr, link->set->name);
Holger Hans Peter Freythereab20962011-01-21 18:00:36 +0100349 }
350}
Holger Hans Peter Freyther71760302011-02-22 20:57:08 +0100351
352struct mtp_udp_link *mtp_udp_link_init(struct mtp_link *blnk)
353{
354 struct bsc_data *bsc;
355 struct mtp_udp_link *lnk;
356
357 lnk = talloc_zero(blnk, struct mtp_udp_link);
358 if (!lnk) {
359 LOGP(DINP, LOGL_ERROR, "Failed to allocate.\n");
360 return NULL;
361 }
362
363 /* setup SNMP first, it is blocking */
364 lnk->session = snmp_mtp_session_create();
365 if (!lnk->session) {
366 LOGP(DINP, LOGL_ERROR, "Failed to allocate snmp session.\n");
367 talloc_free(lnk);
368 return NULL;
369 }
370 lnk->session->data = lnk;
371
372 bsc = blnk->set->bsc;
373 lnk->data = &bsc->udp_data;
374 lnk->reset_timeout = bsc->udp_reset_timeout;
375
376 lnk->base = blnk;
377 lnk->base->data = lnk;
378 lnk->base->type = SS7_LTYPE_UDP;
379 lnk->bsc = bsc;
380
381 /* function table */
382 lnk->base->shutdown = udp_link_shutdown;
383 lnk->base->clear_queue = udp_link_dummy;
384
385 lnk->base->reset = udp_link_reset;
386 lnk->base->start = udp_link_start;
387 lnk->base->write = udp_link_write;
388
389 /* prepare the remote */
390 memset(&lnk->remote, 0, sizeof(lnk->remote));
391 lnk->remote.sin_family = AF_INET;
392
393 /* add it to the list of udp connections */
394 llist_add_tail(&lnk->entry, &lnk->data->links);
395
396 return lnk;
397}