blob: 53da9968be13768ff1944f6ae37a0b8e7d3f5ce3 [file] [log] [blame]
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +02001#include "internal.h"
2
3#include <stdio.h>
4#include <stdbool.h>
5#include <unistd.h>
6#include <stdlib.h>
7#include <errno.h>
8#include <string.h>
9#include <time.h>
10#include <sys/fcntl.h>
11#include <sys/socket.h>
12#include <sys/ioctl.h>
13#include <arpa/inet.h>
14
15#include <osmocom/core/select.h>
16#include <osmocom/core/bitvec.h>
17#include <osmocom/gsm/tlv.h>
18#include <osmocom/core/msgb.h>
19#include <osmocom/core/logging.h>
Harald Welte71d87b22011-07-18 14:49:56 +020020#include <osmocom/core/talloc.h>
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +020021#include <osmocom/abis/e1_input.h>
22#include <osmocom/abis/ipaccess.h>
23#include <osmocom/core/socket.h>
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +020024
25#include <osmocom/abis/ipa.h>
26#include <osmocom/vty/vty.h>
27#include <osmocom/vty/command.h>
28
29static void *tall_ipa_proxy_ctx;
30
31/*
32 * data structures used by the IPA VTY commands
33 */
34static LLIST_HEAD(ipa_instance_list);
35
36enum ipa_proxy_instance_net_type {
37 IPA_INSTANCE_T_NONE,
38 IPA_INSTANCE_T_BIND,
39 IPA_INSTANCE_T_CONNECT,
40 IPA_INSTANCE_T_MAX
41};
42
43struct ipa_proxy_instance_net {
44 char *addr;
45 uint16_t port;
46 enum ipa_proxy_instance_net_type type;
47};
48
49struct ipa_proxy_instance {
50 struct llist_head head;
51#define IPA_INSTANCE_NAME 16
52 char name[IPA_INSTANCE_NAME];
53 struct ipa_proxy_instance_net net;
54 int refcnt;
55};
56
57static LLIST_HEAD(ipa_proxy_route_list);
58
59/* Several routes pointing to the same instances share this. */
60struct ipa_proxy_route_shared {
61 int refcnt;
62
63 /* this file descriptor is used to accept() new connections. */
64 struct osmo_fd bfd;
65
66 struct {
67 struct ipa_proxy_instance *inst;
68 struct bitvec streamid_map;
69 uint8_t streamid_map_data[(0xff+1)/8];
70 uint8_t streamid[0xff];
71 } src;
72 struct {
73 struct ipa_proxy_instance *inst;
74 struct bitvec streamid_map;
75 uint8_t streamid_map_data[(0xff+1)/8];
76 uint8_t streamid[0xff];
77 } dst;
78
79 struct llist_head conn_list;
80};
81
82/* One route is composed of two instances. */
83struct ipa_proxy_route {
84 struct llist_head head;
85
86 struct {
87 uint8_t streamid;
88 } src;
89 struct {
90 uint8_t streamid;
91 } dst;
92
93 struct ipa_proxy_route_shared *shared;
94};
95
96enum ipa_conn_state {
97 IPA_CONN_S_NONE,
98 IPA_CONN_S_CONNECTING,
99 IPA_CONN_S_CONNECTED,
100 IPA_CONN_S_MAX
101};
102
103/* One route may forward more than one connection. */
104struct ipa_proxy_conn {
105 struct llist_head head;
106
107 struct ipa_server_peer *src;
108 struct ipa_client_link *dst;
109 struct ipa_proxy_route *route;
110};
111
112/*
113 * socket callbacks used by IPA VTY commands
114 */
115static int ipa_sock_dst_cb(struct ipa_client_link *link, struct msgb *msg)
116{
117 struct ipaccess_head *hh;
118 struct ipa_proxy_conn *conn = link->data;
119
Harald Weltecc2241b2011-07-19 16:06:06 +0200120 LOGP(DLINP, LOGL_NOTICE, "received message from client side\n");
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200121
122 hh = (struct ipaccess_head *)msg->data;
123 /* check if we have a route for this message. */
124 if (bitvec_get_bit_pos(
125 &conn->route->shared->dst.streamid_map,
126 hh->proto) != ONE) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200127 LOGP(DLINP, LOGL_NOTICE, "we don't have a "
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200128 "route for streamid 0x%x\n", hh->proto);
129 msgb_free(msg);
130 return 0;
131 }
132 /* mangle message, if required. */
133 hh->proto = conn->route->shared->src.streamid[hh->proto];
134
135 ipa_server_peer_send(conn->src, msg);
136 return 0;
137}
138
139static int ipa_sock_src_cb(struct ipa_server_peer *peer, struct msgb *msg)
140{
141 struct ipaccess_head *hh;
142 struct ipa_proxy_conn *conn = peer->data;
143
Harald Weltecc2241b2011-07-19 16:06:06 +0200144 LOGP(DLINP, LOGL_NOTICE, "received message from server side\n");
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200145
146 hh = (struct ipaccess_head *)msg->data;
147 /* check if we have a route for this message. */
148 if (bitvec_get_bit_pos(&conn->route->shared->src.streamid_map,
149 hh->proto) != ONE) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200150 LOGP(DLINP, LOGL_NOTICE, "we don't have a "
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200151 "route for streamid 0x%x\n", hh->proto);
152 msgb_free(msg);
153 return 0;
154 }
155 /* mangle message, if required. */
156 hh->proto = conn->route->shared->dst.streamid[hh->proto];
157
158 ipa_client_link_send(conn->dst, msg);
159 return 0;
160}
161
162static int
163ipa_sock_src_accept_cb(struct ipa_server_link *link, int fd)
164{
165 int ret;
166 struct ipa_proxy_route *route = link->data;
167 struct ipa_proxy_conn *conn;
168
169 conn = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_conn);
170 if (conn == NULL) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200171 LOGP(DLINP, LOGL_ERROR, "cannot allocate memory for "
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200172 "origin IPA\n");
173 close(fd);
174 return ret;
175 }
176 conn->route = route;
177
178 conn->src = ipa_server_peer_create(tall_ipa_proxy_ctx, link, fd,
179 ipa_sock_src_cb, conn);
180 if (conn->src == NULL) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200181 LOGP(DLINP, LOGL_ERROR, "could not create server peer: %s\n",
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200182 strerror(errno));
183 return -ENOMEM;
184 }
185
Harald Weltecc2241b2011-07-19 16:06:06 +0200186 LOGP(DLINP, LOGL_NOTICE, "now trying to connect to destination\n");
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200187
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200188 conn->dst = ipa_client_link_create(NULL, NULL, NULL, 0,
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200189 route->shared->dst.inst->net.addr,
190 route->shared->dst.inst->net.port,
Pablo Neira Ayuso88136fc2011-07-08 16:21:55 +0200191 NULL,
Pablo Neira Ayusoc9c4fd32011-06-30 12:19:42 +0200192 ipa_sock_dst_cb,
193 ipa_client_write_default_cb,
194 conn);
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200195 if (conn->dst == NULL) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200196 LOGP(DLINP, LOGL_ERROR, "could not create client: %s\n",
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200197 strerror(errno));
198 return -ENOMEM;
199 }
200 if (ipa_client_link_open(conn->dst) < 0) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200201 LOGP(DLINP, LOGL_ERROR, "could not start client: %s\n",
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200202 strerror(errno));
203 return -ENOMEM;
204 }
205 llist_add(&conn->head, &route->shared->conn_list);
206 return ret;
207}
208
209/*
210 * VTY commands for IPA
211 */
212DEFUN(ipa_proxy, ipa_cmd, "ipa", "Configure the ipaccess proxy")
213{
214 vty->index = NULL;
Harald Weltecc2241b2011-07-19 16:06:06 +0200215 vty->node = L_IPA_NODE;
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200216 return CMD_SUCCESS;
217}
218
219static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[])
220{
221 struct ipa_proxy_instance *ipi;
222 enum ipa_proxy_instance_net_type type;
223 struct in_addr addr;
224 uint16_t port;
225
226 if (argc < 4)
227 return CMD_ERR_INCOMPLETE;
228
229 llist_for_each_entry(ipi, &ipa_instance_list, head) {
230 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0)
231 continue;
232
233 vty_out(vty, "%% instance `%s' already exists%s",
234 ipi->name, VTY_NEWLINE);
235 return CMD_WARNING;
236 }
237 if (strncmp(argv[1], "bind", IPA_INSTANCE_NAME) == 0)
238 type = IPA_INSTANCE_T_BIND;
239 else if (strncmp(argv[1], "connect", IPA_INSTANCE_NAME) == 0)
240 type = IPA_INSTANCE_T_CONNECT;
241 else
242 return CMD_ERR_INCOMPLETE;
243
244 if (inet_aton(argv[2], &addr) < 0) {
245 vty_out(vty, "%% invalid address %s%s", argv[1], VTY_NEWLINE);
246 return CMD_WARNING;
247 }
248 port = atoi(argv[3]);
249
250 ipi = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_instance);
251 if (ipi == NULL) {
252 vty_out(vty, "%% can't allocate memory for new instance%s",
253 VTY_NEWLINE);
254 return CMD_WARNING;
255 }
256 strncpy(ipi->name, argv[0], IPA_INSTANCE_NAME);
257 ipi->net.type = type;
258 ipi->net.addr = talloc_strdup(tall_ipa_proxy_ctx, argv[2]);
259 ipi->net.port = port;
260 llist_add_tail(&ipi->head, &ipa_instance_list);
261
262 return CMD_SUCCESS;
263}
264
265DEFUN(ipa_instance_add, ipa_instance_add_cmd,
266 "ipa instance NAME (bind|connect) IP tcp port PORT",
267 "Bind or connect instance to address and port")
268{
269 return __ipa_instance_add(vty, argc, argv);
270}
271
272DEFUN(ipa_instance_del, ipa_instance_del_cmd,
273 "no ipa instance NAME",
274 "Delete instance to address and port")
275{
276 struct ipa_proxy_instance *ipi;
277
278 if (argc < 1)
279 return CMD_ERR_INCOMPLETE;
280
281 llist_for_each_entry(ipi, &ipa_instance_list, head) {
282 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0)
283 continue;
284
285 if (ipi->refcnt > 0) {
286 vty_out(vty, "%% instance `%s' is in use%s",
287 ipi->name, VTY_NEWLINE);
288 return CMD_WARNING;
289 }
290 llist_del(&ipi->head);
291 talloc_free(ipi);
292 return CMD_SUCCESS;
293 }
294 vty_out(vty, "%% instance `%s' does not exist%s",
295 ipi->name, VTY_NEWLINE);
296
297 return CMD_WARNING;
298}
299
300DEFUN(ipa_instance_show, ipa_instance_show_cmd,
301 "ipa instance show", "Show existing ipaccess proxy instances")
302{
303 struct ipa_proxy_instance *this;
304
305 llist_for_each_entry(this, &ipa_instance_list, head) {
306 vty_out(vty, "instance %s %s %s tcp port %u%s",
307 this->name, this->net.addr,
308 this->net.type == IPA_INSTANCE_T_BIND ?
309 "bind" : "connect",
310 this->net.port, VTY_NEWLINE);
311 }
312 return CMD_SUCCESS;
313}
314
315static int __ipa_route_add(struct vty *vty, int argc, const char *argv[])
316{
317 struct ipa_proxy_instance *ipi = vty->index;
318 struct ipa_proxy_instance *src = NULL, *dst = NULL;
319 uint32_t src_streamid, dst_streamid;
320 struct ipa_proxy_route *route, *matching_route = NULL;
321 struct ipa_proxy_route_shared *shared = NULL;
322 int ret;
323
324 if (argc < 4)
325 return CMD_ERR_INCOMPLETE;
326
327 llist_for_each_entry(ipi, &ipa_instance_list, head) {
328 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) {
329 src = ipi;
330 continue;
331 }
332 if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) {
333 dst = ipi;
334 continue;
335 }
336 }
337 if (src == NULL) {
338 vty_out(vty, "%% instance `%s' does not exists%s",
339 argv[0], VTY_NEWLINE);
340 return CMD_WARNING;
341 }
342 if (dst == NULL) {
343 vty_out(vty, "%% instance `%s' does not exists%s",
344 argv[2], VTY_NEWLINE);
345 return CMD_WARNING;
346 }
347 if (src->net.type != IPA_INSTANCE_T_BIND) {
348 vty_out(vty, "%% instance `%s' is not of bind type%s",
349 argv[0], VTY_NEWLINE);
350 return CMD_WARNING;
351 }
352 if (dst->net.type != IPA_INSTANCE_T_CONNECT) {
353 vty_out(vty, "%% instance `%s' is not of connect type%s",
354 argv[2], VTY_NEWLINE);
355 return CMD_WARNING;
356 }
357 src_streamid = strtoul(argv[1], NULL, 16);
358 if (src_streamid > 0xff) {
359 vty_out(vty, "%% source streamid must be "
360 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
361 return CMD_WARNING;
362 }
363 dst_streamid = strtoul(argv[3], NULL, 16);
364 if (dst_streamid > 0xff) {
365 vty_out(vty, "%% destination streamid must be "
366 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
367 return CMD_WARNING;
368 }
369 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
370 if (route->shared->src.inst == src &&
371 route->shared->dst.inst == dst) {
372 if (route->src.streamid == src_streamid &&
373 route->dst.streamid == dst_streamid) {
374 vty_out(vty, "%% this route already exists%s",
375 VTY_NEWLINE);
376 return CMD_WARNING;
377 }
378 matching_route = route;
379 break;
380 }
381 }
382 /* new route for this configuration. */
383 route = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route);
384 if (route == NULL) {
385 vty_out(vty, "%% can't allocate memory for new route%s",
386 VTY_NEWLINE);
387 return CMD_WARNING;
388 }
389 route->src.streamid = src_streamid;
390 route->dst.streamid = dst_streamid;
391
392 if (matching_route != NULL) {
393 /* there's already a master route for these configuration. */
394 if (matching_route->shared->src.inst != src) {
395 vty_out(vty, "%% route does not contain "
396 "source instance `%s'%s",
397 argv[0], VTY_NEWLINE);
398 return CMD_WARNING;
399 }
400 if (matching_route->shared->dst.inst != dst) {
401 vty_out(vty, "%% route does not contain "
402 "destination instance `%s'%s",
403 argv[2], VTY_NEWLINE);
404 return CMD_WARNING;
405 }
406 /* use already existing shared routing information. */
407 shared = matching_route->shared;
408 } else {
409 struct ipa_server_link *link;
410
411 /* this is a brand new route, allocate shared routing info. */
412 shared = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route_shared);
413 if (shared == NULL) {
414 vty_out(vty, "%% can't allocate memory for "
415 "new route shared%s", VTY_NEWLINE);
416 return CMD_WARNING;
417 }
418 shared->src.streamid_map.data_len =
419 sizeof(shared->src.streamid_map_data);
420 shared->src.streamid_map.data =
421 shared->src.streamid_map_data;
422 shared->dst.streamid_map.data_len =
423 sizeof(shared->dst.streamid_map_data);
424 shared->dst.streamid_map.data =
425 shared->dst.streamid_map_data;
426
427 link = ipa_server_link_create(tall_ipa_proxy_ctx, NULL,
428 "0.0.0.0",
429 src->net.port,
430 ipa_sock_src_accept_cb, route);
431 if (link == NULL) {
432 vty_out(vty, "%% can't bind instance `%s' to port%s",
433 src->name, VTY_NEWLINE);
434 return CMD_WARNING;
435 }
436 if (ipa_server_link_open(link) < 0) {
437 vty_out(vty, "%% can't bind instance `%s' to port%s",
438 src->name, VTY_NEWLINE);
439 return CMD_WARNING;
440 }
441 INIT_LLIST_HEAD(&shared->conn_list);
442 }
443 route->shared = shared;
444 src->refcnt++;
445 route->shared->src.inst = src;
446 dst->refcnt++;
447 route->shared->dst.inst = dst;
448 shared->src.streamid[src_streamid] = dst_streamid;
449 shared->dst.streamid[dst_streamid] = src_streamid;
450 ret = bitvec_set_bit_pos(&shared->src.streamid_map, src_streamid, ONE);
451 if (ret < 0) {
452 vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE);
453 return CMD_WARNING;
454 }
455 ret = bitvec_set_bit_pos(&shared->dst.streamid_map, dst_streamid, ONE);
456 if (ret < 0) {
457 vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE);
458 return CMD_WARNING;
459 }
460 shared->refcnt++;
461
462 llist_add_tail(&route->head, &ipa_proxy_route_list);
463 return CMD_SUCCESS;
464}
465
466DEFUN(ipa_route_add, ipa_route_add_cmd,
467 "ipa route instance NAME streamid HEXNUM "
468 "instance NAME streamid HEXNUM", "Add IPA route")
469{
470 return __ipa_route_add(vty, argc, argv);
471}
472
473DEFUN(ipa_route_del, ipa_route_del_cmd,
474 "no ipa route instance NAME streamid HEXNUM "
475 "instance NAME streamid HEXNUM", "Delete IPA route")
476{
477 struct ipa_proxy_instance *ipi = vty->index;
478 struct ipa_proxy_instance *src = NULL, *dst = NULL;
479 uint32_t src_streamid, dst_streamid;
480 struct ipa_proxy_route *route, *matching_route = NULL;
481 struct ipa_proxy_conn *conn, *tmp;
482
483 if (argc < 4)
484 return CMD_ERR_INCOMPLETE;
485
486 llist_for_each_entry(ipi, &ipa_instance_list, head) {
487 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) {
488 src = ipi;
489 continue;
490 }
491 if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) {
492 dst = ipi;
493 continue;
494 }
495 }
496 if (src == NULL) {
497 vty_out(vty, "%% instance `%s' does not exists%s",
498 argv[0], VTY_NEWLINE);
499 return CMD_WARNING;
500 }
501 if (dst == NULL) {
502 vty_out(vty, "%% instance `%s' does not exists%s",
503 argv[2], VTY_NEWLINE);
504 return CMD_WARNING;
505 }
506 if (src->net.type != IPA_INSTANCE_T_BIND) {
507 vty_out(vty, "%% instance `%s' is not of bind type%s",
508 argv[0], VTY_NEWLINE);
509 return CMD_WARNING;
510 }
511 if (dst->net.type != IPA_INSTANCE_T_CONNECT) {
512 vty_out(vty, "%% instance `%s' is not of connect type%s",
513 argv[2], VTY_NEWLINE);
514 return CMD_WARNING;
515 }
516 src_streamid = strtoul(argv[1], NULL, 16);
517 if (src_streamid > 0xff) {
518 vty_out(vty, "%% source streamid must be "
519 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
520 return CMD_WARNING;
521 }
522 dst_streamid = strtoul(argv[3], NULL, 16);
523 if (dst_streamid > 0xff) {
524 vty_out(vty, "%% destination streamid must be "
525 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
526 return CMD_WARNING;
527 }
528 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
529 if (route->shared->src.inst == src &&
530 route->shared->dst.inst == dst &&
531 route->src.streamid == src_streamid &&
532 route->dst.streamid == dst_streamid) {
533 matching_route = route;
534 break;
535 }
536 }
537 if (matching_route == NULL) {
538 vty_out(vty, "%% no route with that configuration%s",
539 VTY_NEWLINE);
540 return CMD_WARNING;
541 }
542 /* delete this route from list. */
543 llist_del(&matching_route->head);
544
545 if (--matching_route->shared->refcnt == 0) {
546 /* nobody else using this route, release all resources. */
547 llist_for_each_entry_safe(conn, tmp,
548 &matching_route->shared->conn_list, head) {
549 ipa_server_peer_destroy(conn->src);
550 llist_del(&conn->head);
551 talloc_free(conn);
552 }
553 osmo_fd_unregister(&route->shared->bfd);
554 close(route->shared->bfd.fd);
555 route->shared->bfd.fd = -1;
556
557 talloc_free(route->shared);
558 } else {
559 /* otherwise, revert the mapping that this route applies. */
560 bitvec_set_bit_pos(&matching_route->shared->src.streamid_map,
561 src_streamid, ZERO);
562 bitvec_set_bit_pos(&matching_route->shared->dst.streamid_map,
563 dst_streamid, ZERO);
564 matching_route->shared->src.streamid[src_streamid] = 0x00;
565 matching_route->shared->dst.streamid[dst_streamid] = 0x00;
566 }
567 matching_route->shared->src.inst->refcnt--;
568 matching_route->shared->dst.inst->refcnt--;
569 talloc_free(matching_route);
570
571 return CMD_SUCCESS;
572}
573
574DEFUN(ipa_route_show, ipa_route_show_cmd,
575 "ipa route show", "Show existing ipaccess proxy routes")
576{
577 struct ipa_proxy_route *this;
578
579 llist_for_each_entry(this, &ipa_proxy_route_list, head) {
580 vty_out(vty, "route instance %s streamid 0x%.2x "
581 "instance %s streamid 0x%.2x%s",
582 this->shared->src.inst->name, this->src.streamid,
583 this->shared->dst.inst->name, this->dst.streamid,
584 VTY_NEWLINE);
585 }
586 return CMD_SUCCESS;
587}
588
589/*
590 * Config for ipaccess-proxy
591 */
592DEFUN(ipa_cfg, ipa_cfg_cmd, "ipa", "Configure the ipaccess proxy")
593{
594 vty->index = NULL;
Harald Weltecc2241b2011-07-19 16:06:06 +0200595 vty->node = L_IPA_NODE;
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200596 return CMD_SUCCESS;
597}
598
599/* all these below look like enable commands, but without the ipa prefix. */
600DEFUN(ipa_route_cfg_add, ipa_route_cfg_add_cmd,
601 "route instance NAME streamid HEXNUM "
602 "instance NAME streamid HEXNUM", "Add IPA route")
603{
604 return __ipa_route_add(vty, argc, argv);
605}
606
607DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd,
608 "instance NAME (bind|connect) IP tcp port PORT",
609 "Bind or connect instance to address and port")
610{
611 return __ipa_instance_add(vty, argc, argv);
612}
613
614struct cmd_node ipa_node = {
Harald Weltecc2241b2011-07-19 16:06:06 +0200615 L_IPA_NODE,
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200616 "%s(ipa)#",
617 1,
618};
619
620static int ipa_cfg_write(struct vty *vty)
621{
622 bool heading = false;
623 struct ipa_proxy_instance *inst;
624 struct ipa_proxy_route *route;
625
626 llist_for_each_entry(inst, &ipa_instance_list, head) {
627 if (!heading) {
628 vty_out(vty, "ipa%s", VTY_NEWLINE);
629 heading = true;
630 }
631 vty_out(vty, " instance %s %s %s tcp port %u%s",
632 inst->name,
633 inst->net.type == IPA_INSTANCE_T_BIND ?
634 "bind" : "connect",
635 inst->net.addr,
636 inst->net.port, VTY_NEWLINE);
637 }
638 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
639 vty_out(vty, " route instance %s streamid 0x%.2x "
640 "instance %s streamid 0x%.2x%s",
641 route->shared->src.inst->name, route->src.streamid,
642 route->shared->dst.inst->name, route->dst.streamid,
643 VTY_NEWLINE);
644 }
645 return CMD_SUCCESS;
646}
647
648DEFUN(ournode_exit,
649 ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
650{
651 switch (vty->node) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200652 case L_IPA_NODE:
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200653 vty->node = CONFIG_NODE;
654 vty->index = NULL;
655 break;
656 }
657 return CMD_SUCCESS;
658}
659
660DEFUN(ournode_end,
661 ournode_end_cmd, "end", "End current mode and change to enable mode.\n")
662{
663 switch (vty->node) {
Harald Weltecc2241b2011-07-19 16:06:06 +0200664 case L_IPA_NODE:
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200665 break;
666 }
667 return CMD_SUCCESS;
668}
669
670void ipa_proxy_vty_init(void)
671{
672 tall_ipa_proxy_ctx =
673 talloc_named_const(libosmo_abis_ctx, 1, "ipa_proxy");
674
675 install_element(ENABLE_NODE, &ipa_cmd);
676 install_element(ENABLE_NODE, &ipa_instance_add_cmd);
677 install_element(ENABLE_NODE, &ipa_instance_del_cmd);
678 install_element(ENABLE_NODE, &ipa_instance_show_cmd);
679 install_element(ENABLE_NODE, &ipa_route_add_cmd);
680 install_element(ENABLE_NODE, &ipa_route_del_cmd);
681 install_element(ENABLE_NODE, &ipa_route_show_cmd);
682
683 install_element(CONFIG_NODE, &ipa_cfg_cmd);
684 install_node(&ipa_node, ipa_cfg_write);
Harald Weltecc2241b2011-07-19 16:06:06 +0200685 install_default(L_IPA_NODE);
686 install_element(L_IPA_NODE, &ournode_exit_cmd);
687 install_element(L_IPA_NODE, &ournode_end_cmd);
688 install_element(L_IPA_NODE, &ipa_instance_cfg_add_cmd);
689 install_element(L_IPA_NODE, &ipa_route_cfg_add_cmd);
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200690}