blob: 2ad4654008ad8728be3174e4abff233a5b729681 [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>
20#include <talloc.h>
21#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
120 LOGP(DINP, LOGL_NOTICE, "received message from client side\n");
121
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) {
127 LOGP(DINP, LOGL_NOTICE, "we don't have a "
128 "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
144 LOGP(DINP, LOGL_NOTICE, "received message from server side\n");
145
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) {
150 LOGP(DINP, LOGL_NOTICE, "we don't have a "
151 "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) {
171 LOGP(DINP, LOGL_ERROR, "cannot allocate memory for "
172 "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) {
181 LOGP(DINP, LOGL_ERROR, "could not create server peer: %s\n",
182 strerror(errno));
183 return -ENOMEM;
184 }
185
186 LOGP(DINP, LOGL_NOTICE, "now trying to connect to destination\n");
187
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 Ayusoc9c4fd32011-06-30 12:19:42 +0200191 ipa_sock_dst_cb,
192 ipa_client_write_default_cb,
193 conn);
Pablo Neira Ayuso130c4fb2011-06-23 21:15:53 +0200194 if (conn->dst == NULL) {
195 LOGP(DINP, LOGL_ERROR, "could not create client: %s\n",
196 strerror(errno));
197 return -ENOMEM;
198 }
199 if (ipa_client_link_open(conn->dst) < 0) {
200 LOGP(DINP, LOGL_ERROR, "could not start client: %s\n",
201 strerror(errno));
202 return -ENOMEM;
203 }
204 llist_add(&conn->head, &route->shared->conn_list);
205 return ret;
206}
207
208/*
209 * VTY commands for IPA
210 */
211DEFUN(ipa_proxy, ipa_cmd, "ipa", "Configure the ipaccess proxy")
212{
213 vty->index = NULL;
214 vty->node = IPA_NODE;
215 return CMD_SUCCESS;
216}
217
218static int __ipa_instance_add(struct vty *vty, int argc, const char *argv[])
219{
220 struct ipa_proxy_instance *ipi;
221 enum ipa_proxy_instance_net_type type;
222 struct in_addr addr;
223 uint16_t port;
224
225 if (argc < 4)
226 return CMD_ERR_INCOMPLETE;
227
228 llist_for_each_entry(ipi, &ipa_instance_list, head) {
229 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0)
230 continue;
231
232 vty_out(vty, "%% instance `%s' already exists%s",
233 ipi->name, VTY_NEWLINE);
234 return CMD_WARNING;
235 }
236 if (strncmp(argv[1], "bind", IPA_INSTANCE_NAME) == 0)
237 type = IPA_INSTANCE_T_BIND;
238 else if (strncmp(argv[1], "connect", IPA_INSTANCE_NAME) == 0)
239 type = IPA_INSTANCE_T_CONNECT;
240 else
241 return CMD_ERR_INCOMPLETE;
242
243 if (inet_aton(argv[2], &addr) < 0) {
244 vty_out(vty, "%% invalid address %s%s", argv[1], VTY_NEWLINE);
245 return CMD_WARNING;
246 }
247 port = atoi(argv[3]);
248
249 ipi = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_instance);
250 if (ipi == NULL) {
251 vty_out(vty, "%% can't allocate memory for new instance%s",
252 VTY_NEWLINE);
253 return CMD_WARNING;
254 }
255 strncpy(ipi->name, argv[0], IPA_INSTANCE_NAME);
256 ipi->net.type = type;
257 ipi->net.addr = talloc_strdup(tall_ipa_proxy_ctx, argv[2]);
258 ipi->net.port = port;
259 llist_add_tail(&ipi->head, &ipa_instance_list);
260
261 return CMD_SUCCESS;
262}
263
264DEFUN(ipa_instance_add, ipa_instance_add_cmd,
265 "ipa instance NAME (bind|connect) IP tcp port PORT",
266 "Bind or connect instance to address and port")
267{
268 return __ipa_instance_add(vty, argc, argv);
269}
270
271DEFUN(ipa_instance_del, ipa_instance_del_cmd,
272 "no ipa instance NAME",
273 "Delete instance to address and port")
274{
275 struct ipa_proxy_instance *ipi;
276
277 if (argc < 1)
278 return CMD_ERR_INCOMPLETE;
279
280 llist_for_each_entry(ipi, &ipa_instance_list, head) {
281 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) != 0)
282 continue;
283
284 if (ipi->refcnt > 0) {
285 vty_out(vty, "%% instance `%s' is in use%s",
286 ipi->name, VTY_NEWLINE);
287 return CMD_WARNING;
288 }
289 llist_del(&ipi->head);
290 talloc_free(ipi);
291 return CMD_SUCCESS;
292 }
293 vty_out(vty, "%% instance `%s' does not exist%s",
294 ipi->name, VTY_NEWLINE);
295
296 return CMD_WARNING;
297}
298
299DEFUN(ipa_instance_show, ipa_instance_show_cmd,
300 "ipa instance show", "Show existing ipaccess proxy instances")
301{
302 struct ipa_proxy_instance *this;
303
304 llist_for_each_entry(this, &ipa_instance_list, head) {
305 vty_out(vty, "instance %s %s %s tcp port %u%s",
306 this->name, this->net.addr,
307 this->net.type == IPA_INSTANCE_T_BIND ?
308 "bind" : "connect",
309 this->net.port, VTY_NEWLINE);
310 }
311 return CMD_SUCCESS;
312}
313
314static int __ipa_route_add(struct vty *vty, int argc, const char *argv[])
315{
316 struct ipa_proxy_instance *ipi = vty->index;
317 struct ipa_proxy_instance *src = NULL, *dst = NULL;
318 uint32_t src_streamid, dst_streamid;
319 struct ipa_proxy_route *route, *matching_route = NULL;
320 struct ipa_proxy_route_shared *shared = NULL;
321 int ret;
322
323 if (argc < 4)
324 return CMD_ERR_INCOMPLETE;
325
326 llist_for_each_entry(ipi, &ipa_instance_list, head) {
327 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) {
328 src = ipi;
329 continue;
330 }
331 if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) {
332 dst = ipi;
333 continue;
334 }
335 }
336 if (src == NULL) {
337 vty_out(vty, "%% instance `%s' does not exists%s",
338 argv[0], VTY_NEWLINE);
339 return CMD_WARNING;
340 }
341 if (dst == NULL) {
342 vty_out(vty, "%% instance `%s' does not exists%s",
343 argv[2], VTY_NEWLINE);
344 return CMD_WARNING;
345 }
346 if (src->net.type != IPA_INSTANCE_T_BIND) {
347 vty_out(vty, "%% instance `%s' is not of bind type%s",
348 argv[0], VTY_NEWLINE);
349 return CMD_WARNING;
350 }
351 if (dst->net.type != IPA_INSTANCE_T_CONNECT) {
352 vty_out(vty, "%% instance `%s' is not of connect type%s",
353 argv[2], VTY_NEWLINE);
354 return CMD_WARNING;
355 }
356 src_streamid = strtoul(argv[1], NULL, 16);
357 if (src_streamid > 0xff) {
358 vty_out(vty, "%% source streamid must be "
359 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
360 return CMD_WARNING;
361 }
362 dst_streamid = strtoul(argv[3], NULL, 16);
363 if (dst_streamid > 0xff) {
364 vty_out(vty, "%% destination streamid must be "
365 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
366 return CMD_WARNING;
367 }
368 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
369 if (route->shared->src.inst == src &&
370 route->shared->dst.inst == dst) {
371 if (route->src.streamid == src_streamid &&
372 route->dst.streamid == dst_streamid) {
373 vty_out(vty, "%% this route already exists%s",
374 VTY_NEWLINE);
375 return CMD_WARNING;
376 }
377 matching_route = route;
378 break;
379 }
380 }
381 /* new route for this configuration. */
382 route = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route);
383 if (route == NULL) {
384 vty_out(vty, "%% can't allocate memory for new route%s",
385 VTY_NEWLINE);
386 return CMD_WARNING;
387 }
388 route->src.streamid = src_streamid;
389 route->dst.streamid = dst_streamid;
390
391 if (matching_route != NULL) {
392 /* there's already a master route for these configuration. */
393 if (matching_route->shared->src.inst != src) {
394 vty_out(vty, "%% route does not contain "
395 "source instance `%s'%s",
396 argv[0], VTY_NEWLINE);
397 return CMD_WARNING;
398 }
399 if (matching_route->shared->dst.inst != dst) {
400 vty_out(vty, "%% route does not contain "
401 "destination instance `%s'%s",
402 argv[2], VTY_NEWLINE);
403 return CMD_WARNING;
404 }
405 /* use already existing shared routing information. */
406 shared = matching_route->shared;
407 } else {
408 struct ipa_server_link *link;
409
410 /* this is a brand new route, allocate shared routing info. */
411 shared = talloc_zero(tall_ipa_proxy_ctx, struct ipa_proxy_route_shared);
412 if (shared == NULL) {
413 vty_out(vty, "%% can't allocate memory for "
414 "new route shared%s", VTY_NEWLINE);
415 return CMD_WARNING;
416 }
417 shared->src.streamid_map.data_len =
418 sizeof(shared->src.streamid_map_data);
419 shared->src.streamid_map.data =
420 shared->src.streamid_map_data;
421 shared->dst.streamid_map.data_len =
422 sizeof(shared->dst.streamid_map_data);
423 shared->dst.streamid_map.data =
424 shared->dst.streamid_map_data;
425
426 link = ipa_server_link_create(tall_ipa_proxy_ctx, NULL,
427 "0.0.0.0",
428 src->net.port,
429 ipa_sock_src_accept_cb, route);
430 if (link == NULL) {
431 vty_out(vty, "%% can't bind instance `%s' to port%s",
432 src->name, VTY_NEWLINE);
433 return CMD_WARNING;
434 }
435 if (ipa_server_link_open(link) < 0) {
436 vty_out(vty, "%% can't bind instance `%s' to port%s",
437 src->name, VTY_NEWLINE);
438 return CMD_WARNING;
439 }
440 INIT_LLIST_HEAD(&shared->conn_list);
441 }
442 route->shared = shared;
443 src->refcnt++;
444 route->shared->src.inst = src;
445 dst->refcnt++;
446 route->shared->dst.inst = dst;
447 shared->src.streamid[src_streamid] = dst_streamid;
448 shared->dst.streamid[dst_streamid] = src_streamid;
449 ret = bitvec_set_bit_pos(&shared->src.streamid_map, src_streamid, ONE);
450 if (ret < 0) {
451 vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE);
452 return CMD_WARNING;
453 }
454 ret = bitvec_set_bit_pos(&shared->dst.streamid_map, dst_streamid, ONE);
455 if (ret < 0) {
456 vty_out(vty, "%% bad bitmask (?)%s", VTY_NEWLINE);
457 return CMD_WARNING;
458 }
459 shared->refcnt++;
460
461 llist_add_tail(&route->head, &ipa_proxy_route_list);
462 return CMD_SUCCESS;
463}
464
465DEFUN(ipa_route_add, ipa_route_add_cmd,
466 "ipa route instance NAME streamid HEXNUM "
467 "instance NAME streamid HEXNUM", "Add IPA route")
468{
469 return __ipa_route_add(vty, argc, argv);
470}
471
472DEFUN(ipa_route_del, ipa_route_del_cmd,
473 "no ipa route instance NAME streamid HEXNUM "
474 "instance NAME streamid HEXNUM", "Delete IPA route")
475{
476 struct ipa_proxy_instance *ipi = vty->index;
477 struct ipa_proxy_instance *src = NULL, *dst = NULL;
478 uint32_t src_streamid, dst_streamid;
479 struct ipa_proxy_route *route, *matching_route = NULL;
480 struct ipa_proxy_conn *conn, *tmp;
481
482 if (argc < 4)
483 return CMD_ERR_INCOMPLETE;
484
485 llist_for_each_entry(ipi, &ipa_instance_list, head) {
486 if (strncmp(ipi->name, argv[0], IPA_INSTANCE_NAME) == 0) {
487 src = ipi;
488 continue;
489 }
490 if (strncmp(ipi->name, argv[2], IPA_INSTANCE_NAME) == 0) {
491 dst = ipi;
492 continue;
493 }
494 }
495 if (src == NULL) {
496 vty_out(vty, "%% instance `%s' does not exists%s",
497 argv[0], VTY_NEWLINE);
498 return CMD_WARNING;
499 }
500 if (dst == NULL) {
501 vty_out(vty, "%% instance `%s' does not exists%s",
502 argv[2], VTY_NEWLINE);
503 return CMD_WARNING;
504 }
505 if (src->net.type != IPA_INSTANCE_T_BIND) {
506 vty_out(vty, "%% instance `%s' is not of bind type%s",
507 argv[0], VTY_NEWLINE);
508 return CMD_WARNING;
509 }
510 if (dst->net.type != IPA_INSTANCE_T_CONNECT) {
511 vty_out(vty, "%% instance `%s' is not of connect type%s",
512 argv[2], VTY_NEWLINE);
513 return CMD_WARNING;
514 }
515 src_streamid = strtoul(argv[1], NULL, 16);
516 if (src_streamid > 0xff) {
517 vty_out(vty, "%% source streamid must be "
518 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
519 return CMD_WARNING;
520 }
521 dst_streamid = strtoul(argv[3], NULL, 16);
522 if (dst_streamid > 0xff) {
523 vty_out(vty, "%% destination streamid must be "
524 ">= 0x00 and <= 0xff%s", VTY_NEWLINE);
525 return CMD_WARNING;
526 }
527 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
528 if (route->shared->src.inst == src &&
529 route->shared->dst.inst == dst &&
530 route->src.streamid == src_streamid &&
531 route->dst.streamid == dst_streamid) {
532 matching_route = route;
533 break;
534 }
535 }
536 if (matching_route == NULL) {
537 vty_out(vty, "%% no route with that configuration%s",
538 VTY_NEWLINE);
539 return CMD_WARNING;
540 }
541 /* delete this route from list. */
542 llist_del(&matching_route->head);
543
544 if (--matching_route->shared->refcnt == 0) {
545 /* nobody else using this route, release all resources. */
546 llist_for_each_entry_safe(conn, tmp,
547 &matching_route->shared->conn_list, head) {
548 ipa_server_peer_destroy(conn->src);
549 llist_del(&conn->head);
550 talloc_free(conn);
551 }
552 osmo_fd_unregister(&route->shared->bfd);
553 close(route->shared->bfd.fd);
554 route->shared->bfd.fd = -1;
555
556 talloc_free(route->shared);
557 } else {
558 /* otherwise, revert the mapping that this route applies. */
559 bitvec_set_bit_pos(&matching_route->shared->src.streamid_map,
560 src_streamid, ZERO);
561 bitvec_set_bit_pos(&matching_route->shared->dst.streamid_map,
562 dst_streamid, ZERO);
563 matching_route->shared->src.streamid[src_streamid] = 0x00;
564 matching_route->shared->dst.streamid[dst_streamid] = 0x00;
565 }
566 matching_route->shared->src.inst->refcnt--;
567 matching_route->shared->dst.inst->refcnt--;
568 talloc_free(matching_route);
569
570 return CMD_SUCCESS;
571}
572
573DEFUN(ipa_route_show, ipa_route_show_cmd,
574 "ipa route show", "Show existing ipaccess proxy routes")
575{
576 struct ipa_proxy_route *this;
577
578 llist_for_each_entry(this, &ipa_proxy_route_list, head) {
579 vty_out(vty, "route instance %s streamid 0x%.2x "
580 "instance %s streamid 0x%.2x%s",
581 this->shared->src.inst->name, this->src.streamid,
582 this->shared->dst.inst->name, this->dst.streamid,
583 VTY_NEWLINE);
584 }
585 return CMD_SUCCESS;
586}
587
588/*
589 * Config for ipaccess-proxy
590 */
591DEFUN(ipa_cfg, ipa_cfg_cmd, "ipa", "Configure the ipaccess proxy")
592{
593 vty->index = NULL;
594 vty->node = IPA_NODE;
595 return CMD_SUCCESS;
596}
597
598/* all these below look like enable commands, but without the ipa prefix. */
599DEFUN(ipa_route_cfg_add, ipa_route_cfg_add_cmd,
600 "route instance NAME streamid HEXNUM "
601 "instance NAME streamid HEXNUM", "Add IPA route")
602{
603 return __ipa_route_add(vty, argc, argv);
604}
605
606DEFUN(ipa_instance_cfg_add, ipa_instance_cfg_add_cmd,
607 "instance NAME (bind|connect) IP tcp port PORT",
608 "Bind or connect instance to address and port")
609{
610 return __ipa_instance_add(vty, argc, argv);
611}
612
613struct cmd_node ipa_node = {
614 IPA_NODE,
615 "%s(ipa)#",
616 1,
617};
618
619static int ipa_cfg_write(struct vty *vty)
620{
621 bool heading = false;
622 struct ipa_proxy_instance *inst;
623 struct ipa_proxy_route *route;
624
625 llist_for_each_entry(inst, &ipa_instance_list, head) {
626 if (!heading) {
627 vty_out(vty, "ipa%s", VTY_NEWLINE);
628 heading = true;
629 }
630 vty_out(vty, " instance %s %s %s tcp port %u%s",
631 inst->name,
632 inst->net.type == IPA_INSTANCE_T_BIND ?
633 "bind" : "connect",
634 inst->net.addr,
635 inst->net.port, VTY_NEWLINE);
636 }
637 llist_for_each_entry(route, &ipa_proxy_route_list, head) {
638 vty_out(vty, " route instance %s streamid 0x%.2x "
639 "instance %s streamid 0x%.2x%s",
640 route->shared->src.inst->name, route->src.streamid,
641 route->shared->dst.inst->name, route->dst.streamid,
642 VTY_NEWLINE);
643 }
644 return CMD_SUCCESS;
645}
646
647DEFUN(ournode_exit,
648 ournode_exit_cmd, "exit", "Exit current mode and down to previous mode\n")
649{
650 switch (vty->node) {
651 case IPA_NODE:
652 vty->node = CONFIG_NODE;
653 vty->index = NULL;
654 break;
655 }
656 return CMD_SUCCESS;
657}
658
659DEFUN(ournode_end,
660 ournode_end_cmd, "end", "End current mode and change to enable mode.\n")
661{
662 switch (vty->node) {
663 case IPA_NODE:
664 break;
665 }
666 return CMD_SUCCESS;
667}
668
669void ipa_proxy_vty_init(void)
670{
671 tall_ipa_proxy_ctx =
672 talloc_named_const(libosmo_abis_ctx, 1, "ipa_proxy");
673
674 install_element(ENABLE_NODE, &ipa_cmd);
675 install_element(ENABLE_NODE, &ipa_instance_add_cmd);
676 install_element(ENABLE_NODE, &ipa_instance_del_cmd);
677 install_element(ENABLE_NODE, &ipa_instance_show_cmd);
678 install_element(ENABLE_NODE, &ipa_route_add_cmd);
679 install_element(ENABLE_NODE, &ipa_route_del_cmd);
680 install_element(ENABLE_NODE, &ipa_route_show_cmd);
681
682 install_element(CONFIG_NODE, &ipa_cfg_cmd);
683 install_node(&ipa_node, ipa_cfg_write);
684 install_default(IPA_NODE);
685 install_element(IPA_NODE, &ournode_exit_cmd);
686 install_element(IPA_NODE, &ournode_end_cmd);
687 install_element(IPA_NODE, &ipa_instance_cfg_add_cmd);
688 install_element(IPA_NODE, &ipa_route_cfg_add_cmd);
689}