#include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include "gtp.h" #define TUN_STR "tun device commands\n" #define GTP_EP_STR "GTP endpoint commands\n" #define TUNNEL_STR "GTP tunnel commands\n" static void show_tun_hdr(struct vty *vty) { vty_out(vty, " tun device name | netwk namespace | use count%s", VTY_NEWLINE); vty_out(vty, "---------------- | ---------------- | ---------%s", VTY_NEWLINE); } static void show_one_tun(struct vty *vty, const struct tun_device *tun) { vty_out(vty, "%16s | %16s | %lu%s", tun->devname, tun->netns_name, tun->use_count, VTY_NEWLINE); } DEFUN(show_tun, show_tun_cmd, "show tun-device [IFNAME]", SHOW_STR TUN_STR "Name of TUN network device\n") { struct tun_device *tun; show_tun_hdr(vty); pthread_rwlock_rdlock(&g_daemon->rwlock); if (argc) { tun = _tun_device_find(g_daemon, argv[0]); if (!tun) { pthread_rwlock_unlock(&g_daemon->rwlock); vty_out(vty, "Cannot find TUN device '%s'%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } show_one_tun(vty, tun); } else { llist_for_each_entry(tun, &g_daemon->tun_devices, list) show_one_tun(vty, tun); } pthread_rwlock_unlock(&g_daemon->rwlock); return CMD_SUCCESS; } DEFUN(tun_create, tun_create_cmd, "tun-device create IFNAME [NETNS]", TUN_STR "Create a new TUN interface\n" "Name of TUN network device\n" "Name of network namespace for tun device\n" ) { struct tun_device *tun; const char *ifname = argv[0]; const char *netns_name = NULL; if (argc > 1) netns_name = argv[1]; tun = tun_device_find_or_create(g_daemon, ifname, netns_name); if (!tun) { vty_out(vty, "Error creating TUN%s", VTY_NEWLINE); return CMD_WARNING; } return CMD_SUCCESS; } DEFUN(tun_destroy, tun_destroy_cmd, "tun-device destroy IFNAME", TUN_STR "Destroy a TUN interface\n" "Name of TUN network device\n" ) { struct tun_device *tun; const char *ifname = argv[0]; pthread_rwlock_wrlock(&g_daemon->rwlock); tun = _tun_device_find(g_daemon, ifname); if (!tun) { pthread_rwlock_unlock(&g_daemon->rwlock); vty_out(vty, "Cannot destrory non-existant TUN%s", VTY_NEWLINE); return CMD_WARNING; } _tun_device_deref_release(tun); pthread_rwlock_unlock(&g_daemon->rwlock); return CMD_SUCCESS; } static void show_ep_hdr(struct vty *vty) { vty_out(vty, " address port | use count%s", VTY_NEWLINE); vty_out(vty, " ------------------------------- | ---------%s", VTY_NEWLINE); } static void show_one_ep(struct vty *vty, const struct gtp_endpoint *ep) { vty_out(vty, "%32s | %lu%s", ep->name, ep->use_count, VTY_NEWLINE); } DEFUN(show_gtp, show_gtp_cmd, "show gtp-endpoint [(A.B.C.D|X:X::X:X) [<0-65535>]]", SHOW_STR GTP_EP_STR "Local IP address\n" "Local UDP Port\n") { struct gtp_endpoint *ep; struct addrinfo *ai = NULL; const char *ipstr; uint16_t port = GTP1U_PORT; if (argc > 0) { ipstr = argv[0]; if (argc > 1) port = atoi(argv[1]); ai = addrinfo_helper(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, ipstr, port, true); if (!ai) { vty_out(vty, "Error parsing IP/Port%s", VTY_NEWLINE); return CMD_WARNING; } } show_ep_hdr(vty); pthread_rwlock_rdlock(&g_daemon->rwlock); if (argc > 0) { ep = _gtp_endpoint_find(g_daemon, (const struct sockaddr_storage *) ai->ai_addr); if (!ep) { pthread_rwlock_unlock(&g_daemon->rwlock); vty_out(vty, "Cannot find GTP endpoint %s:%s%s", argv[0], argv[1], VTY_NEWLINE); freeaddrinfo(ai); return CMD_WARNING; } show_one_ep(vty, ep); } else { llist_for_each_entry(ep, &g_daemon->gtp_endpoints, list) show_one_ep(vty, ep); } pthread_rwlock_unlock(&g_daemon->rwlock); freeaddrinfo(ai); return CMD_SUCCESS; } DEFUN(gtp_create, gtp_create_cmd, "gtp-endpoint create (A.B.C.D|X:X::X:X) [<0-65535>]", GTP_EP_STR "Create a new GTP endpoint (UDP socket)\n" "Local IP address\n" "Local UDP Port\n") { struct addrinfo *ai; struct gtp_endpoint *ep; const char *ipstr = argv[0]; uint16_t port = GTP1U_PORT; if (argc > 1) port = atoi(argv[1]); ai = addrinfo_helper(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, ipstr, port, true); if (!ai) { vty_out(vty, "Error parsing IP/Port%s", VTY_NEWLINE); return CMD_WARNING; } ep = gtp_endpoint_find_or_create(g_daemon, (struct sockaddr_storage *) ai->ai_addr); if (!ep) { vty_out(vty, "Error creating endpoint%s", VTY_NEWLINE); freeaddrinfo(ai); return CMD_WARNING; } freeaddrinfo(ai); return CMD_SUCCESS; } DEFUN(gtp_destroy, gtp_destroy_cmd, "gtp-endpoint destroy (A.B.C.D|X:X::X:X) [<0-65535>]", GTP_EP_STR "Destroy a GTP endpoint\n" "Local IP address\n" "Local UDP Port\n") { struct addrinfo *ai; struct gtp_endpoint *ep; const char *ipstr = argv[0]; uint16_t port = GTP1U_PORT; if (argc > 1) port = atoi(argv[1]); ai = addrinfo_helper(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, ipstr, port, true); if (!ai) { vty_out(vty, "Error parsing IP/Port%s", VTY_NEWLINE); return CMD_WARNING; } pthread_rwlock_wrlock(&g_daemon->rwlock); ep = _gtp_endpoint_find(g_daemon, (struct sockaddr_storage *) ai->ai_addr); if (!ep) { pthread_rwlock_unlock(&g_daemon->rwlock); vty_out(vty, "Cannot find to-be-destoryed endpoint%s", VTY_NEWLINE); freeaddrinfo(ai); return CMD_WARNING; } _gtp_endpoint_deref_destroy(ep); pthread_rwlock_unlock(&g_daemon->rwlock); freeaddrinfo(ai); return CMD_SUCCESS; } static void show_one_tunnel(struct vty *vty, const struct gtp_tunnel *t) { char remote_ip[64], remote_port[16], user_addr[64]; getnameinfo((struct sockaddr *) &t->remote_udp, sizeof(t->remote_udp), remote_ip, sizeof(remote_ip), remote_port, sizeof(remote_port), NI_NUMERICHOST|NI_NUMERICSERV); getnameinfo((struct sockaddr *) &t->user_addr, sizeof(t->user_addr), user_addr, sizeof(user_addr), NULL, 0, NI_NUMERICHOST|NI_NUMERICSERV); vty_out(vty, "%s/%08X - %s:%s/%08X %s(%s) %s%s", t->gtp_ep->name, t->rx_teid, remote_ip, remote_port, t->tx_teid, t->tun_dev->devname, t->tun_dev->netns_name, user_addr, VTY_NEWLINE); } DEFUN(show_tunnel, show_tunnel_cmd, "show gtp-tunnel", SHOW_STR TUNNEL_STR) { struct gtp_tunnel *t; pthread_rwlock_rdlock(&g_daemon->rwlock); llist_for_each_entry(t, &g_daemon->gtp_tunnels, list) { show_one_tunnel(vty, t); } pthread_rwlock_unlock(&g_daemon->rwlock); return CMD_SUCCESS; } #define UECUPS_NODE (_LAST_OSMOVTY_NODE+1) static struct cmd_node uecups_node = { UECUPS_NODE, "%s(config-uecups)# ", 1, }; static int config_write_uecups(struct vty *vty) { vty_out(vty, "uecups%s", VTY_NEWLINE); vty_out(vty, " local-ip %s%s", g_daemon->cfg.cups_local_ip, VTY_NEWLINE); return CMD_SUCCESS; } DEFUN(cfg_uecups, cfg_uecups_cmd, "uecups", "Configure the UE Control/User Plane Socket\n") { vty->node = UECUPS_NODE; return CMD_SUCCESS; } DEFUN(cfg_uecups_local_ip, cfg_uecups_local_ip_cmd, "local-ip A.B.C.D", "Set the IP address to which we bind locally\n" "IP Address\n") { osmo_talloc_replace_string(g_daemon, &g_daemon->cfg.cups_local_ip, argv[0]); return CMD_SUCCESS; } int gtpud_vty_init(void) { install_element_ve(&show_tun_cmd); install_element(ENABLE_NODE, &tun_create_cmd); install_element(ENABLE_NODE, &tun_destroy_cmd); install_element_ve(&show_gtp_cmd); install_element(ENABLE_NODE, >p_create_cmd); install_element(ENABLE_NODE, >p_destroy_cmd); install_element_ve(&show_tunnel_cmd); install_element(CONFIG_NODE, &cfg_uecups_cmd); install_node(&uecups_node, config_write_uecups); install_element(UECUPS_NODE, &cfg_uecups_local_ip_cmd); return 0; } static const char copyright[] = "Copyright (C) 2020 Harald Welte \r\n" "License GPLv2: GNU GPL version 2 \r\n" "This is free software: you are free to change and redistribute it.\r\n" "There is NO WARRANTY, to the extent permitted by law.\r\n"; struct vty_app_info g_vty_info = { .name = "osmo-gtpud", .version = PACKAGE_VERSION, .copyright = copyright, };