9
0
Fork 0

bsc: Remove the global link_set pointer from the bsc

Start removing the static names for the linkset
This commit is contained in:
Holger Hans Peter Freyther 2011-02-10 18:26:07 +01:00
parent 43b015a8ea
commit 89fa11af02
7 changed files with 127 additions and 97 deletions

View File

@ -123,8 +123,8 @@ struct bsc_data {
int setup; int setup;
int pcap_fd; int pcap_fd;
int udp_reset_timeout; int udp_reset_timeout;
struct mtp_link_set *link_set;
struct mtp_link_set *m2ua_set; struct mtp_link_set *m2ua_set;
struct llist_head links;
/* udp code */ /* udp code */
struct mtp_udp_data udp_data; struct mtp_udp_data udp_data;
@ -176,7 +176,7 @@ void update_con_state(struct bsc_msc_forward *fw, int rc, struct sccp_parse_resu
/* udp init */ /* udp init */
int link_global_init(struct mtp_udp_data *data, int src_port); int link_global_init(struct mtp_udp_data *data, int src_port);
int link_udp_init(struct mtp_udp_link *data, char *dest_ip, int port); int link_udp_init(struct mtp_udp_link *data, char *dest_ip, int port);
int link_init(struct bsc_data *bsc); struct mtp_link_set *link_init(struct bsc_data *bsc);
int link_shutdown_all(struct mtp_link_set *); int link_shutdown_all(struct mtp_link_set *);
int link_reset_all(struct mtp_link_set *); int link_reset_all(struct mtp_link_set *);
int link_clear_all(struct mtp_link_set *); int link_clear_all(struct mtp_link_set *);

View File

@ -41,6 +41,8 @@ struct rate_ctr_group;
* The state of the mtp_link in terms of layer3 and upwards * The state of the mtp_link in terms of layer3 and upwards
*/ */
struct mtp_link_set { struct mtp_link_set {
struct llist_head entry;
/* routing info.. */ /* routing info.. */
int dpc, opc, sccp_opc, isup_opc; int dpc, opc, sccp_opc, isup_opc;
int ni; int ni;
@ -73,6 +75,7 @@ struct mtp_link_set {
/* custom data */ /* custom data */
struct bsc_data *bsc; struct bsc_data *bsc;
struct bsc_msc_forward *fw; struct bsc_msc_forward *fw;
struct mtp_link_set *forward;
}; };
/** /**

View File

@ -100,47 +100,48 @@ static void start_rest(void *_set)
data->start(data); data->start(data);
} }
int link_init(struct bsc_data *bsc) struct mtp_link_set *link_init(struct bsc_data *bsc)
{ {
int i; int i;
struct mtp_udp_link *lnk; struct mtp_udp_link *lnk;
struct mtp_link_set *set;
bsc->link_set = mtp_link_set_alloc(); set = mtp_link_set_alloc();
bsc->link_set->name = talloc_strdup(bsc->link_set, "MTP"); set->name = talloc_strdup(set, "MTP");
bsc->link_set->dpc = bsc->dpc; set->dpc = bsc->dpc;
bsc->link_set->opc = bsc->opc; set->opc = bsc->opc;
bsc->link_set->sccp_opc = bsc->sccp_opc > -1 ? bsc->sccp_opc : bsc->opc; set->sccp_opc = bsc->sccp_opc > -1 ? bsc->sccp_opc : bsc->opc;
bsc->link_set->isup_opc = bsc->isup_opc > -1 ? bsc->isup_opc : bsc->opc; set->isup_opc = bsc->isup_opc > -1 ? bsc->isup_opc : bsc->opc;
bsc->link_set->sltm_once = bsc->once; set->sltm_once = bsc->once;
bsc->link_set->ni = bsc->ni_ni; set->ni = bsc->ni_ni;
bsc->link_set->spare = bsc->ni_spare; set->spare = bsc->ni_spare;
bsc->link_set->bsc = bsc; set->bsc = bsc;
bsc->link_set->pcap_fd = bsc->pcap_fd; set->pcap_fd = bsc->pcap_fd;
if (!bsc->src_port) { if (!bsc->src_port) {
LOGP(DINP, LOGL_ERROR, "You need to set a UDP address.\n"); LOGP(DINP, LOGL_ERROR, "You need to set a UDP address.\n");
return -1; return NULL;
} }
LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n"); LOGP(DINP, LOGL_NOTICE, "Using UDP MTP mode.\n");
if (link_global_init(&bsc->udp_data, bsc->src_port) != 0) if (link_global_init(&bsc->udp_data, bsc->src_port) != 0)
return -1; return NULL;
for (i = 1; i <= bsc->udp_nr_links; ++i) { for (i = 1; i <= bsc->udp_nr_links; ++i) {
lnk = talloc_zero(bsc->link_set, struct mtp_udp_link); lnk = talloc_zero(set, struct mtp_udp_link);
lnk->base.pcap_fd = -1; lnk->base.pcap_fd = -1;
lnk->bsc = bsc; lnk->bsc = bsc;
lnk->data = &bsc->udp_data; lnk->data = &bsc->udp_data;
lnk->link_index = i; lnk->link_index = i;
lnk->reset_timeout = bsc->udp_reset_timeout; lnk->reset_timeout = bsc->udp_reset_timeout;
mtp_link_set_add_link(bsc->link_set, (struct mtp_link *) lnk); mtp_link_set_add_link(set, (struct mtp_link *) lnk);
/* now connect to the transport */ /* now connect to the transport */
if (link_udp_init(lnk, bsc->udp_ip, bsc->udp_port) != 0) if (link_udp_init(lnk, bsc->udp_ip, bsc->udp_port) != 0)
return -1; return NULL;
/* /*
* We will ask the MTP link to be taken down for two * We will ask the MTP link to be taken down for two
@ -156,10 +157,10 @@ int link_init(struct bsc_data *bsc)
} }
bsc->start_timer.cb = start_rest; bsc->start_timer.cb = start_rest;
bsc->start_timer.data = bsc->link_set; bsc->start_timer.data = set;
bsc_schedule_timer(&bsc->start_timer, lnk->reset_timeout, 0); bsc_schedule_timer(&bsc->start_timer, bsc->udp_reset_timeout, 0);
return 0; return set;
} }
int link_shutdown_all(struct mtp_link_set *set) int link_shutdown_all(struct mtp_link_set *set)

View File

@ -74,6 +74,8 @@ static void sigint()
static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
static int handled = 0; static int handled = 0;
struct mtp_link_set *set;
/* failed to lock */ /* failed to lock */
if (pthread_mutex_trylock(&exit_mutex) != 0) if (pthread_mutex_trylock(&exit_mutex) != 0)
return; return;
@ -82,8 +84,11 @@ static void sigint()
printf("Terminating.\n"); printf("Terminating.\n");
handled = 1; handled = 1;
if (bsc.setup) if (bsc.setup) {
link_shutdown_all(bsc.link_set); llist_for_each_entry(set, &bsc.links, entry)
link_shutdown_all(set);
}
exit(0); exit(0);
out: out:
@ -167,6 +172,8 @@ static void bsc_msc_forward_init(struct bsc_data *bsc,
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int rc; int rc;
struct mtp_link_set *set;
INIT_LLIST_HEAD(&bsc.links);
bsc.app = APP_CELLMGR; bsc.app = APP_CELLMGR;
bsc.dpc = 1; bsc.dpc = 1;
@ -221,10 +228,13 @@ int main(int argc, char **argv)
if (rc < 0) if (rc < 0)
return rc; return rc;
if (link_init(&bsc) != 0) set = link_init(&bsc);
if (!set)
return -1; return -1;
bsc.link_set->fw = &bsc.msc_forward;
bsc.msc_forward.bsc = bsc.link_set; llist_add(&set->entry, &bsc.links);
set->fw = &bsc.msc_forward;
bsc.msc_forward.bsc = set;
while (1) { while (1) {
bsc_select_main(0); bsc_select_main(0);

View File

@ -69,20 +69,14 @@ extern void cell_vty_init(void);
/* /*
* methods called from the MTP Level3 part * methods called from the MTP Level3 part
*/ */
void mtp_link_set_forward_sccp(struct mtp_link_set *link, struct msgb *_msg, int sls) void mtp_link_set_forward_sccp(struct mtp_link_set *set, struct msgb *_msg, int sls)
{ {
struct mtp_link_set *target; mtp_link_set_submit_sccp_data(set->forward, sls, _msg->l2h, msgb_l2len(_msg));
target = bsc.m2ua_set == link ? bsc.link_set : bsc.m2ua_set;
mtp_link_set_submit_sccp_data(target, sls, _msg->l2h, msgb_l2len(_msg));
} }
void mtp_link_set_forward_isup(struct mtp_link_set *set, struct msgb *msg, int sls) void mtp_link_set_forward_isup(struct mtp_link_set *set, struct msgb *msg, int sls)
{ {
struct mtp_link_set *target; mtp_link_set_submit_isup_data(set->forward, sls, msg->l3h, msgb_l3len(msg));
target = bsc.m2ua_set == set ? bsc.link_set : bsc.m2ua_set;
mtp_link_set_submit_isup_data(target, sls, msg->l3h, msgb_l3len(msg));
} }
void mtp_linkset_down(struct mtp_link_set *set) void mtp_linkset_down(struct mtp_link_set *set)
@ -107,6 +101,8 @@ static void sigint()
static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
static int handled = 0; static int handled = 0;
struct mtp_link_set *set;
/* failed to lock */ /* failed to lock */
if (pthread_mutex_trylock(&exit_mutex) != 0) if (pthread_mutex_trylock(&exit_mutex) != 0)
return; return;
@ -115,8 +111,10 @@ static void sigint()
printf("Terminating.\n"); printf("Terminating.\n");
handled = 1; handled = 1;
if (bsc.setup) if (bsc.setup) {
link_shutdown_all(bsc.link_set); llist_for_each_entry(set, &bsc.links, entry)
link_shutdown_all(set);
}
exit(0); exit(0);
out: out:
@ -182,10 +180,12 @@ static void handle_options(int argc, char **argv)
static struct mtp_link_set *find_link_set(struct bsc_data *bsc, static struct mtp_link_set *find_link_set(struct bsc_data *bsc,
int len, const char *buf) int len, const char *buf)
{ {
if (strncmp(buf, "mtp", len) == 0) struct mtp_link_set *set;
return bsc->link_set;
else if (strncmp(buf, "m2ua", len) == 0) llist_for_each_entry(set, &bsc->links, entry)
return bsc->m2ua_set; if (strncmp(buf, set->name, len) == 0)
return set;
return NULL; return NULL;
} }
@ -304,7 +304,9 @@ int main(int argc, char **argv)
{ {
int rc; int rc;
struct mtp_link *data; struct mtp_link *data;
struct mtp_link_set *set;
struct mtp_m2ua_link *lnk; struct mtp_m2ua_link *lnk;
INIT_LLIST_HEAD(&bsc.links);
bsc.app = APP_STP; bsc.app = APP_STP;
bsc.dpc = 1; bsc.dpc = 1;
@ -361,8 +363,10 @@ int main(int argc, char **argv)
return -1; return -1;
} }
if (link_init(&bsc) != 0) set = link_init(&bsc);
if (!set)
return -1; return -1;
llist_add(&set->entry, &bsc.links);
bsc.m2ua_set = mtp_link_set_alloc(); bsc.m2ua_set = mtp_link_set_alloc();
bsc.m2ua_set->dpc = 92; bsc.m2ua_set->dpc = 92;
@ -373,13 +377,13 @@ int main(int argc, char **argv)
bsc.m2ua_set->bsc = &bsc; bsc.m2ua_set->bsc = &bsc;
bsc.m2ua_set->pcap_fd = bsc.pcap_fd; bsc.m2ua_set->pcap_fd = bsc.pcap_fd;
bsc.m2ua_set->name = talloc_strdup(bsc.m2ua_set, "M2UA"); bsc.m2ua_set->name = talloc_strdup(bsc.m2ua_set, "M2UA");
llist_add(&bsc.m2ua_set->entry, &bsc.links);
/* for both links we want to have all isup messages */ /* setup things */
if (bsc.isup_pass) { set->pass_all_isup = bsc.isup_pass;
LOGP(DINP, LOGL_NOTICE, "Going to pass through all ISUP messages.\n"); set->forward = bsc.m2ua_set;
bsc.m2ua_set->pass_all_isup = 1; bsc.m2ua_set->pass_all_isup = bsc.isup_pass;
bsc.link_set->pass_all_isup = 1; bsc.m2ua_set->forward = set;
}
lnk = sctp_m2ua_transp_create("0.0.0.0", 2904); lnk = sctp_m2ua_transp_create("0.0.0.0", 2904);
lnk->base.pcap_fd = -1; lnk->base.pcap_fd = -1;

View File

@ -102,6 +102,8 @@ static void sigint()
static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t exit_mutex = PTHREAD_MUTEX_INITIALIZER;
static int handled = 0; static int handled = 0;
struct mtp_link_set *set;
/* failed to lock */ /* failed to lock */
if (pthread_mutex_trylock(&exit_mutex) != 0) if (pthread_mutex_trylock(&exit_mutex) != 0)
return; return;
@ -110,8 +112,10 @@ static void sigint()
printf("Terminating.\n"); printf("Terminating.\n");
handled = 1; handled = 1;
if (bsc.setup) if (bsc.setup) {
link_shutdown_all(bsc.link_set); llist_for_each_entry(set, &bsc.links, entry)
link_shutdown_all(set);
}
exit(0); exit(0);
out: out:
@ -195,6 +199,8 @@ static void bsc_msc_forward_init(struct bsc_data *bsc,
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int rc; int rc;
struct mtp_link_set *set;
INIT_LLIST_HEAD(&bsc.links);
bsc.app = APP_RELAY; bsc.app = APP_RELAY;
bsc.dpc = 1; bsc.dpc = 1;
@ -250,10 +256,13 @@ int main(int argc, char **argv)
if (rc < 0) if (rc < 0)
return rc; return rc;
if (link_init(&bsc) != 0) set = link_init(&bsc);
if (!set)
return -1; return -1;
bsc.link_set->fw = &bsc.msc_forward;
bsc.msc_forward.bsc = bsc.link_set; llist_add(&set->entry, &bsc.links);
set->fw = &bsc.msc_forward;
bsc.msc_forward.bsc = set;
while (1) { while (1) {
bsc_select_main(0); bsc_select_main(0);

View File

@ -297,20 +297,21 @@ DEFUN(cfg_isup_pass, cfg_isup_pass_cmd,
"Pass through all ISUP messages directly\n" "Pass through all ISUP messages directly\n"
"Handle some messages locally\n" "Pass through everything\n") "Handle some messages locally\n" "Pass through everything\n")
{ {
struct mtp_link_set *set;
bsc.isup_pass = atoi(argv[0]); bsc.isup_pass = atoi(argv[0]);
if (bsc.m2ua_set)
bsc.m2ua_set->pass_all_isup = bsc.isup_pass; llist_for_each_entry(set, &bsc.links, entry)
if (bsc.link_set) set->pass_all_isup = bsc.isup_pass;
bsc.link_set->pass_all_isup = bsc.isup_pass;
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static void dump_stats(struct vty *vty, const char *name, struct mtp_link_set *set) static void dump_stats(struct vty *vty, struct mtp_link_set *set)
{ {
struct mtp_link *link; struct mtp_link *link;
vty_out(vty, "Linkset name: %s opc: %d%s", name, set->opc, VTY_NEWLINE); vty_out(vty, "Linkset name: %s opc: %d%s", set->name, set->opc, VTY_NEWLINE);
vty_out_rate_ctr_group(vty, " ", set->ctrg); vty_out_rate_ctr_group(vty, " ", set->ctrg);
llist_for_each_entry(link, &set->links, entry) { llist_for_each_entry(link, &set->links, entry) {
@ -323,24 +324,25 @@ DEFUN(show_stats, show_stats_cmd,
"show statistics", "show statistics",
SHOW_STR "Display Linkset statistics\n") SHOW_STR "Display Linkset statistics\n")
{ {
if (bsc.link_set) struct mtp_link_set *set;
dump_stats(vty, "MTP ", bsc.link_set);
if (bsc.m2ua_set && bsc.app == APP_STP) llist_for_each_entry(set, &bsc.links, entry)
dump_stats(vty, "M2UA", bsc.m2ua_set); dump_stats(vty, set);
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static void dump_state(struct vty *vty, const char *name, struct mtp_link_set *set) static void dump_state(struct vty *vty, struct mtp_link_set *set)
{ {
struct mtp_link *link; struct mtp_link *link;
if (!set) { if (!set) {
vty_out(vty, "LinkSet for %s is not configured.%s", name, VTY_NEWLINE); vty_out(vty, "LinkSet for %s is not configured.%s", set->name, VTY_NEWLINE);
return; return;
} }
vty_out(vty, "LinkSet for %s is %s, remote sccp is %s.%s", vty_out(vty, "LinkSet for %s is %s, remote sccp is %s.%s",
name, set->name,
set->available == 0 ? "not available" : "available", set->available == 0 ? "not available" : "available",
set->sccp_up == 0? "not established" : "established", set->sccp_up == 0? "not established" : "established",
VTY_NEWLINE); VTY_NEWLINE);
@ -361,9 +363,10 @@ DEFUN(show_linksets, show_linksets_cmd,
"show link-sets", "show link-sets",
SHOW_STR "Display current state of linksets\n") SHOW_STR "Display current state of linksets\n")
{ {
dump_state(vty, "MTP ", bsc.link_set); struct mtp_link_set *set;
if (bsc.app == APP_STP)
dump_state(vty, "M2UA", bsc.m2ua_set); llist_for_each_entry(set, &bsc.links, entry)
dump_state(vty, set);
return CMD_SUCCESS; return CMD_SUCCESS;
} }
@ -378,17 +381,26 @@ DEFUN(show_msc, show_msc_cmd,
return CMD_SUCCESS; return CMD_SUCCESS;
} }
static struct mtp_link_set *find_link_set(struct llist_head *head,
const char *name)
{
struct mtp_link_set *set;
llist_for_each_entry(set, head, entry)
if (strcmp(name, set->name) == 0)
return set;
return NULL;
}
DEFUN(show_slc, show_slc_cmd, DEFUN(show_slc, show_slc_cmd,
"show link-set (mtp|m2ua) slc", "show link-set NAME slc",
SHOW_STR "LinkSet\n" "MTP Linkset\n" "M2UA LinkSet\n" "SLS to SLC\n") SHOW_STR "LinkSet\n" "Linkset name\n" "SLS to SLC\n")
{ {
struct mtp_link_set *set = NULL; struct mtp_link_set *set = NULL;
int i; int i;
if (bsc.link_set && strcmp(argv[0], "mtp") == 0) set = find_link_set(&bsc.links, argv[0]);
set = bsc.link_set;
else if (bsc.m2ua_set && strcmp(argv[0], "m2ua") == 0)
set = bsc.m2ua_set;
if (!set) { if (!set) {
vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE); vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE);
@ -409,16 +421,13 @@ DEFUN(show_slc, show_slc_cmd,
} }
DEFUN(pcap_set, pcap_set_cmd, DEFUN(pcap_set, pcap_set_cmd,
"trace-pcap set (m2ua|mtp) FILE", "trace-pcap set NAME FILE",
"Trace to a PCAP file\n" "Trace a linkset\n" "Trace to a PCAP file\n" "Trace a linkset\n"
"Trace m2ua linkset\n" "Trace mtp linkset\n" "Filename to trace\n") "Trace Linkset\n" "Filename to trace\n")
{ {
struct mtp_link_set *set = NULL; struct mtp_link_set *set = NULL;
if (bsc.link_set && strcmp(argv[0], "mtp") == 0) set = find_link_set(&bsc.links, argv[0]);
set = bsc.link_set;
else if (bsc.m2ua_set && strcmp(argv[0], "m2ua") == 0)
set = bsc.m2ua_set;
if (!set) { if (!set) {
vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE); vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE);
@ -440,16 +449,13 @@ DEFUN(pcap_set, pcap_set_cmd,
} }
DEFUN(pcap_set_stop, pcap_set_stop_cmd, DEFUN(pcap_set_stop, pcap_set_stop_cmd,
"trace-pcap set (m2ua|mtp) stop", "trace-pcap set NAME stop",
"Trace to a PCAP file\n" "Trace a linkset\n" "Trace to a PCAP file\n" "Trace a linkset\n"
"Trace m2ua linkset\n" "Trace mtp linkset\n" "Stop the tracing\n") "Trace Linkset\n" "Stop the tracing\n")
{ {
struct mtp_link_set *set = NULL; struct mtp_link_set *set = NULL;
if (bsc.link_set && strcmp(argv[0], "mtp") == 0) set = find_link_set(&bsc.links, argv[0]);
set = bsc.link_set;
else if (bsc.m2ua_set && strcmp(argv[0], "m2ua") == 0)
set = bsc.m2ua_set;
if (!set) { if (!set) {
vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE); vty_out(vty, "Failed to find linkset.%s", VTY_NEWLINE);
@ -465,11 +471,8 @@ DEFUN(pcap_set_stop, pcap_set_stop_cmd,
#define FIND_LINK(vty, type, nr) ({ \ #define FIND_LINK(vty, type, nr) ({ \
struct mtp_link_set *set = NULL; \ struct mtp_link_set *set = NULL; \
struct mtp_link *link = NULL, *tmp; \ struct mtp_link *link = NULL, *tmp; \
if (strcmp(type, "mtp") == 0) \ set = find_link_set(&bsc.links, type); \
set = bsc.link_set; \ if (!set) { \
else if (strcmp(type, "m2ua") == 0) \
set = bsc.m2ua_set; \
else { \
vty_out(vty, "Unknown linkset %s.%s", type, VTY_NEWLINE); \ vty_out(vty, "Unknown linkset %s.%s", type, VTY_NEWLINE); \
return CMD_WARNING; \ return CMD_WARNING; \
} \ } \
@ -486,11 +489,11 @@ DEFUN(pcap_set_stop, pcap_set_stop_cmd,
link; }) link; })
#define LINK_STR "Operations on the link\n" \ #define LINK_STR "Operations on the link\n" \
"MTP Linkset\n" "M2UA Linkset\n" \ "Linkset name\n" \
"Link number\n" "Link number\n"
DEFUN(lnk_block, lnk_block_cmd, DEFUN(lnk_block, lnk_block_cmd,
"link (mtp|m2ua) <0-15> block", "link NAME <0-15> block",
LINK_STR "Block it\n") LINK_STR "Block it\n")
{ {
struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1])); struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1]));
@ -499,7 +502,7 @@ DEFUN(lnk_block, lnk_block_cmd,
} }
DEFUN(lnk_unblock, lnk_unblock_cmd, DEFUN(lnk_unblock, lnk_unblock_cmd,
"link (mtp|m2ua) <0-15> unblock", "link NAME <0-15> unblock",
LINK_STR "Unblock it\n") LINK_STR "Unblock it\n")
{ {
struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1])); struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1]));
@ -508,7 +511,7 @@ DEFUN(lnk_unblock, lnk_unblock_cmd,
} }
DEFUN(lnk_reset, lnk_reset_cmd, DEFUN(lnk_reset, lnk_reset_cmd,
"link (mtp|m2ua) <0-15> reset", "link NAME <0-15> reset",
LINK_STR "Reset it\n") LINK_STR "Reset it\n")
{ {
struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1])); struct mtp_link *link = FIND_LINK(vty, argv[0], atoi(argv[1]));