/* AX25 control commands * Copyright 1991 Phil Karn, KA9Q */ /* Mods by G1EMM */ /* Mods by N1BEE */ /* ** FILE: ax25cmd.c ** ** AX.25 command handler. ** ** 09/24/90 Bob Applegate, wa2zzx ** Added BCTEXT, BC, and BCINTERVAL commands for broadcasting an id ** string using UI frames. ** ** 27/09/91 Mike Bilow, N1BEE ** Added Filter command for axheard control */ #include #include "global.h" #include "config.h" #include "mbuf.h" #include "timer.h" #include "proc.h" #include "iface.h" #include "ax25.h" #include "lapb.h" #include "cmdparse.h" #include "socket.h" #include "mailbox.h" #include "session.h" #include "tty.h" #include "nr4.h" #include "commands.h" #include "pktdrvr.h" static int axheard __ARGS((struct iface *ifp)); static void axflush __ARGS((struct iface *ifp)); static int doaxfilter __ARGS((int argc,char *argv[],void *p)); static int doaxflush __ARGS((int argc,char *argv[],void *p)); static int doaxirtt __ARGS((int argc,char *argv[],void *p)); static int doaxkick __ARGS((int argc,char *argv[],void *p)); static int doaxreset __ARGS((int argc,char *argv[],void *p)); static int doaxroute __ARGS((int argc,char *argv[],void *p)); static int doaxstat __ARGS((int argc,char *argv[],void *p)); static int doaxwindow __ARGS((int argc,char *argv[],void *p)); static int dobc __ARGS((int argc,char *argv[],void *p)); static int dobcint __ARGS((int argc,char *argv[],void *p)); static int dobctext __ARGS((int argc,char *argv[],void *p)); static int doblimit __ARGS((int argc,char *argv[],void *p)); static int dodigipeat __ARGS((int argc,char *argv[],void *p)); static int domaxframe __ARGS((int argc,char *argv[],void *p)); static int domycall __ARGS((int argc,char *argv[],void *p)); static int don2 __ARGS((int argc,char *argv[],void *p)); static int dopaclen __ARGS((int argc,char *argv[],void *p)); static int dopthresh __ARGS((int argc,char *argv[],void *p)); static int dot3 __ARGS((int argc,char *argv[],void *p)); static int doaxtype __ARGS((int argc,char *argv[],void *p)); static int dot4 __ARGS((int argc,char *argv[],void *p)); static int doversion __ARGS((int argc,char *argv[],void *p)); static void ax_bc __ARGS((struct iface *axif)); static int axdest __ARGS((struct iface *ifp)); extern int axheard_filter_flag; /* in axheard.c */ /* ** Default broadcast 'to' address in shifted ASCII */ char ax_bcto[AXALEN] = { 'I'<<1, 'D'<<1, ' '<<1, ' '<<1, ' '<<1, ' '<<1, ('0'<<1) | E }; /* ** Defaults for IDing... */ char *axbctext = NULL; /* Text to send */ static struct timer Broadtimer; /* timer for broadcasts */ char *Ax25states[] = { "", "Disconnected", "Listening", "Conn pending", "Disc pending", "Connected", "Recovery", }; /* Ascii explanations for the disconnect reasons listed in lapb.h under * "reason" in ax25_cb */ char *Axreasons[] = { "Normal", "DM received", "Timeout" }; static struct cmds Axcmds[] = { "bc", dobc, 0, 0, NULLCHAR, "bcinterval", dobcint, 0, 0, NULLCHAR, "blimit", doblimit, 0, 0, NULLCHAR, "bctext", dobctext, 0, 0, NULLCHAR, "digipeat", dodigipeat, 0, 0, NULLCHAR, "filter", doaxfilter, 0, 0, NULLCHAR, "flush", doaxflush, 0, 0, NULLCHAR, "heard", doaxheard, 0, 0, NULLCHAR, "hearddest", doaxdest, 0, 0, NULLCHAR, "irtt", doaxirtt, 0, 0, NULLCHAR, "kick", doaxkick, 0, 2, "ax25 kick ", "maxframe", domaxframe, 0, 0, NULLCHAR, "mycall", domycall, 0, 0, NULLCHAR, "paclen", dopaclen, 0, 0, NULLCHAR, "pthresh", dopthresh, 0, 0, NULLCHAR, "reset", doaxreset, 0, 2, "ax25 reset ", "retry", don2, 0, 0, NULLCHAR, "route", doaxroute, 0, 0, NULLCHAR, "status", doaxstat, 0, 0, NULLCHAR, "t3", dot3, 0, 0, NULLCHAR, "t4", dot4, 0, 0, NULLCHAR, "timertype", doaxtype, 0, 0, NULLCHAR, "version", doversion, 0, 0, NULLCHAR, "window", doaxwindow, 0, 0, NULLCHAR, NULLCHAR, }; /* Multiplexer for top-level ax25 command */ int doax25(argc,argv,p) int argc; char *argv[]; void *p; { return subcmd(Axcmds,argc,argv,p); } /* ** This function is called to send the current broadcast message ** and reset the timer. */ static int dobc(argc,argv,p) int argc; char *argv[]; void *p; { struct iface *ifa; if (argc < 2) { printf("you need to specify an interface\n"); return 1; } ifa = Ifaces; while (ifa != NULL && stricmp(ifa->name,argv[1])) ifa = ifa->next; if (ifa == NULL) printf("unknown interface\n"); else if (ifa->type != CL_AX25) printf("not an AX.25 interface\n"); else { ax_bc(ifa); stop_timer(&Broadtimer) ; /* in case it's already running */ start_timer(&Broadtimer); /* and fire it up */ } return 0; } /* ** View/Change the message we broadcast. */ static int dobctext(argc,argv,p) int argc; char *argv[]; void *p; { if (argc < 2) printf("Broadcast text: %s\n",axbctext); else { if (axbctext != NULL) free(axbctext); axbctext = malloc(strlen(argv[1])); strcpy(axbctext,argv[1]); } return 0; } #define TICKSPERSEC (1000L / MSPTICK) /* Ticks per second */ /* ** Examine/change the broadcast interval. */ static int dobcint(argc,argv,p) int argc; char *argv[]; void *p; { void dobroadtick(); if(argc < 2) { tprintf("Broadcast timer %lu/%lu seconds\n", read_timer(&Broadtimer)/1000L, dur_timer(&Broadtimer)/1000L); return 0; } stop_timer(&Broadtimer) ; /* in case it's already running */ Broadtimer.func = (void (*)())dobroadtick;/* what to call on timeout */ Broadtimer.arg = NULLCHAR; /* dummy value */ set_timer(&Broadtimer,atoi(argv[1])*1000L); /* set timer duration */ start_timer(&Broadtimer); /* and fire it up */ return 0; } void dobroadtick() { struct iface *ifa; ifa = Ifaces; while (ifa != NULL) { if (ifa->type == CL_AX25) ax_bc(ifa); ifa = ifa->next; } /* Restart timer */ start_timer(&Broadtimer) ; } /* ** This is the low-level broadcast function. */ static void ax_bc(axif) struct iface *axif; { struct mbuf *hbp; int i; /* prepare the header */ i = strlen(axbctext); if((hbp = alloc_mbuf(i)) == NULLBUF) return; hbp->cnt = i; memcpy(hbp->data,axbctext,i); (*axif->output)(axif, ax_bcto, axif->hwaddr, PID_NO_L3, hbp); /* send it */ /* ** Call another function to reset the timer... reset_bc_timer(); */ } int doaxheard(argc,argv,p) int argc; char *argv[]; void *p; { struct iface *ifp; if(argc > 1){ if((ifp = if_lookup(argv[1])) == NULLIF){ tprintf("Interface %s unknown\n",argv[1]); return 1; } if(ifp->output != ax_output){ tprintf("Interface %s not AX.25\n",argv[1]); return 1; } axheard(ifp); return 0; } for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){ if(ifp->output != ax_output) continue; /* Not an ax.25 interface */ if(axheard(ifp) == EOF) break; } return 0; } static int axheard(ifp) struct iface *ifp; { int i, col = 0; struct lq *lp; char tmp[AXBUF]; if(ifp->hwaddr == NULLCHAR) return 0; tprintf("Interface Station Time since send Pkts sent\n"); tprintf("%-9s %-9s %12s %7lu\n",ifp->name,pax25(tmp,ifp->hwaddr), tformat(secclock() - ifp->lastsent),ifp->rawsndcnt); tprintf("Station Time since heard Pkts rcvd : "); tprintf("Station Time since heard Pkts rcvd\n"); for(lp = Lq;lp != NULLLQ;lp = lp->next){ if(lp->iface != ifp) continue; if(col) tprintf(" : "); if(tprintf("%-9s %12s %7lu",pax25(tmp,lp->addr), tformat(secclock() - lp->time),lp->currxcnt) == EOF) return EOF; if(col){ if(tprintf("\n") == EOF){ return EOF; } else { col = 0; } } else { col = 1; } } if(col) tprintf("\n"); return 0; } int doaxdest(argc,argv,p) int argc; char *argv[]; void *p; { struct iface *ifp; if(argc > 1){ if((ifp = if_lookup(argv[1])) == NULLIF){ tprintf("Interface %s unknown\n",argv[1]); return 1; } if(ifp->output != ax_output){ tprintf("Interface %s not AX.25\n",argv[1]); return 1; } axdest(ifp); return 0; } for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){ if(ifp->output != ax_output) continue; /* Not an ax.25 interface */ if(axdest(ifp) == EOF) break; } return 0; } static int axdest(ifp) struct iface *ifp; { struct ld *lp; struct lq *lq; char tmp[AXBUF]; if(ifp->hwaddr == NULLCHAR) return 0; tprintf("%s:\n",ifp->name); tprintf("Station Last ref Last heard Pkts\n"); for(lp = Ld;lp != NULLLD;lp = lp->next){ if(lp->iface != ifp) continue; tprintf("%-10s%-17s", pax25(tmp,lp->addr),tformat(secclock() - lp->time)); if(addreq(lp->addr,ifp->hwaddr)){ /* Special case; it's our address */ tprintf("%-17s",tformat(secclock() - ifp->lastsent)); } else if((lq = al_lookup(ifp,lp->addr,0)) == NULLLQ){ tprintf("%-17s",""); } else { tprintf("%-17s",tformat(secclock() - lq->time)); } if(tprintf("%8lu\n",lp->currxcnt) == EOF) return EOF; } return 0; } static int doaxfilter(argc,argv,p) int argc; char *argv[]; void *p; { if(argc >= 2){ setint(&axheard_filter_flag,"ax25 heard filter",argc,argv); } else { tprintf("Usage: ax25 filter <0|1|2|3>\n"); return 1; } tprintf("Callsign loggin by source "); if(axheard_filter_flag & AXHEARD_NOSRC) tprintf("disabled, "); else tprintf("enabled, "); tprintf("by destination "); if(axheard_filter_flag & AXHEARD_NODST) tprintf("disabled\n"); else tprintf("enabled\n"); return 0; } static int doaxflush(argc,argv,p) int argc; char *argv[]; void *p; { struct iface *ifp; for(ifp = Ifaces;ifp != NULLIF;ifp = ifp->next){ if(ifp->output != ax_output) continue; /* Not an ax.25 interface */ axflush(ifp); } return 0; } static void axflush(ifp) struct iface *ifp; { struct lq *lp,*lp1; struct ld *ld,*ld1; ifp->rawsndcnt = 0; for(lp = Lq;lp != NULLLQ;lp = lp1){ lp1 = lp->next; free((char *)lp); } Lq = NULLLQ; for(ld = Ld;ld != NULLLD;ld = ld1){ ld1 = ld->next; free((char *)ld); } Ld = NULLLD; } static doaxreset(argc,argv,p) int argc; char *argv[]; void *p; { struct ax25_cb *axp; axp = (struct ax25_cb *)ltop(htol(argv[1])); if(!ax25val(axp)){ tprintf(Notval); return 1; } reset_ax25(axp); return 0; } /* Display AX.25 link level control blocks */ static doaxstat(argc,argv,p) int argc; char *argv[]; void *p; { register struct ax25_cb *axp; char tmp[AXBUF]; if(argc < 2){ tprintf(" &AXB Snd-Q Rcv-Q Remote State\n"); for(axp = Ax25_cb;axp != NULLAX25; axp = axp->next){ if(tprintf("%8lx %-8d%-8d%-10s%s\n", ptol(axp), len_q(axp->txq),len_p(axp->rxq), pax25(tmp,axp->remote), Ax25states[axp->state]) == EOF) return 0; } return 0; } axp = (struct ax25_cb *)ltop(htol(argv[1])); if(!ax25val(axp)){ tprintf(Notval); return 1; } st_ax25(axp); return 0; } /* Dump one control block */ void st_ax25(axp) register struct ax25_cb *axp; { char tmp[AXBUF]; if(axp == NULLAX25) return; tprintf(" &AXB Remote RB V(S) V(R) Unack P Retry State\n"); tprintf("%8lx %-9s%c%c",ptol(axp),pax25(tmp,axp->remote), axp->flags.rejsent ? 'R' : ' ', axp->flags.remotebusy ? 'B' : ' '); tprintf(" %4d %4d",axp->vs,axp->vr); tprintf(" %02u/%02u %u",axp->unack,axp->maxframe,axp->proto); tprintf(" %02u/%02u",axp->retries,axp->n2); tprintf(" %s\n",Ax25states[axp->state]); tprintf("srtt = %lu mdev = %lu ",axp->srt,axp->mdev); tprintf("T1: "); if(run_timer(&axp->t1)) tprintf("%lu",read_timer(&axp->t1)); else tprintf("stop"); tprintf("/%lu ms; ",dur_timer(&axp->t1)); tprintf("T3: "); if(run_timer(&axp->t3)) tprintf("%lu",read_timer(&axp->t3)); else tprintf("stop"); tprintf("/%lu ms; ",dur_timer(&axp->t3)); tprintf("T4: "); if(run_timer(&axp->t4)) tprintf("%lu",(read_timer(&axp->t4))); else tprintf("stop"); tprintf("/%lu sec\n",(dur_timer(&axp->t4))); } /* Set limit on retransmission backoff */ static doblimit(argc,argv,p) int argc; char *argv[]; void *p; { return setlong(&Blimit,"blimit",argc,argv); } /* Display or change our AX.25 address */ static domycall(argc,argv,p) int argc; char *argv[]; void *p; { char tmp[AXBUF]; if(argc < 2){ tprintf("%s\n",pax25(tmp,Mycall)); return 0; } if(setcall(Mycall,argv[1]) == -1) return -1; return 0; } /* Control AX.25 digipeating */ static dodigipeat(argc,argv,p) int argc; char *argv[]; void *p; { return setbool(&Digipeat,"Digipeat",argc,argv); } static doversion(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&Axversion,"AX25 version",argc,argv); } static doaxirtt(argc,argv,p) int argc; char *argv[]; void *p; { return setlong(&Axirtt,"Initial RTT (ms)",argc,argv); } /* Set idle timer */ static dot3(argc,argv,p) int argc; char *argv[]; void *p; { return setlong(&T3init,"Idle poll timer (ms)",argc,argv); } /* Set link redundancy timer */ static dot4(argc,argv,p) int argc; char *argv[]; void *p; { return setlong(&T4init,"Link redundancy timer (sec)",argc,argv); } /* Set retry limit count */ static don2(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&N2,"Retry limit",argc,argv); } /* Force a retransmission */ static doaxkick(argc,argv,p) int argc; char *argv[]; void *p; { struct ax25_cb *axp; axp = (struct ax25_cb *)ltop(htol(argv[1])); if(!ax25val(axp)){ tprintf(Notval); return 1; } kick_ax25(axp); return 0; } /* Set maximum number of frames that will be allowed in flight */ static domaxframe(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&Maxframe,"Window size (frames)",argc,argv); } /* Set maximum length of I-frame data field */ static dopaclen(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&Paclen,"Max frame length (bytes)",argc,argv); } /* Set size of I-frame above which polls will be sent after a timeout */ static dopthresh(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&Pthresh,"Poll threshold (bytes)",argc,argv); } /* Set high water mark on receive queue that triggers RNR */ static doaxwindow(argc,argv,p) int argc; char *argv[]; void *p; { return setshort(&Axwindow,"AX25 receive window (bytes)",argc,argv); } /* End of ax25 subcommands */ /* Initiate interactive AX.25 connect to remote station */ int doconnect(argc,argv,p) int argc; char *argv[]; void *p; { struct sockaddr_ax fsocket; struct session *sp; struct iface *ifp; int ndigis,i; char digis[MAXDIGIS][AXALEN]; char target[AXALEN]; if(((ifp = if_lookup(argv[1])) != NULLIF) && (ifp->type != CL_AX25)) { tprintf("Iface %s not an AX25 type interface\n",argv[1]); return 1; } if(setcall(target,argv[2]) == -1){ tprintf("Bad callsign %s\n", argv[2]); return 1; } /* If digipeaters are given, put them in the routing table */ if(argc > 3){ if(setcall(target,argv[2]) == -1){ tprintf("Bad callsign %s\n", argv[2]); return 1; } ndigis = argc - 3; if(ndigis > MAXDIGIS){ tprintf("Too many digipeaters\n"); return 1; } for(i=0;is = socket(AF_AX25,SOCK_STREAM,0)) == -1){ tprintf("Can't create socket\n"); freesession(sp); keywait(NULLCHAR,1); return 1; } fsocket.sax_family = AF_AX25; setcall(fsocket.ax25_addr,argv[2]); strncpy(fsocket.iface,argv[1],ILEN); return tel_connect(sp, (char *)&fsocket, sizeof(struct sockaddr_ax)); } /* Display and modify AX.25 routing table */ static int doaxroute(argc,argv,p) int argc; char *argv[]; void *p; { char tmp[AXBUF]; int i,ndigis; register struct ax_route *axr; char target[AXALEN],digis[MAXDIGIS][AXALEN]; if(argc < 2){ tprintf("Target Type Mode Digipeaters\n"); for(axr = Ax_routes;axr != NULLAXR;axr = axr->next){ tprintf("%-10s%-6s",pax25(tmp,axr->target), axr->type == AX_LOCAL ? "Local":"Auto"); switch(axr->mode){ case AX_VC_MODE: tprintf(" VC "); break; case AX_DATMODE: tprintf(" DG "); break; case AX_DEFMODE: tprintf(" IF "); break; default: tprintf(" ?? "); break; } for(i=0;indigis;i++){ tprintf(" %s",pax25(tmp,axr->digis[i])); } if(tprintf("\n") == EOF) return 0; } return 0; } if(argc < 3){ tprintf("Usage: ax25 route add [digis...]\n"); tprintf(" ax25 route drop \n"); tprintf(" ax25 route mode [mode]\n"); return 1; } if(setcall(target,argv[2]) == -1){ tprintf("Bad target %s\n",argv[2]); return 1; } switch(argv[1][0]){ case 'a': /* Add route */ ndigis = argc - 3; if(ndigis > MAXDIGIS){ tprintf("Too many digipeaters\n"); return 1; } for(i=0;i \n"); tprintf("Where mode is 'vc', 'datagram' or 'interface'\n"); return 1; } if((axr = ax_lookup(target)) == NULLAXR){ tprintf("Not in table\n"); return 1; } switch(argv[3][0]){ case 'i': /* use default interface mode */ axr->mode = AX_DEFMODE; break; case 'v': /* use virtual circuit mode */ axr->mode = AX_VC_MODE; break; case 'd': /* use datagram mode */ axr->mode = AX_DATMODE; break; default: tprintf("Unknown mode %s\n", argv[3]); return 1; } break; default: tprintf("Unknown command %s\n",argv[1]); return 1; } return 0; } /* ax25 timers type - linear v exponential */ static doaxtype(argc,argv,p) int argc ; char *argv[] ; void *p ; { extern unsigned lapbtimertype; if (argc < 2) { tprintf("AX25 timer type is "); switch(lapbtimertype){ case 2: tprintf("original\n"); break; case 1: tprintf("linear\n"); break; case 0: tprintf("exponential\n"); break; } return 0 ; } switch (argv[1][0]) { case 'o': case 'O': lapbtimertype = 2 ; break ; case 'l': case 'L': lapbtimertype = 1 ; break ; case 'e': case 'E': lapbtimertype = 0 ; break ; default: tprintf("use: ax25 timertype [original|linear|exponential]\n") ; return -1 ; } return 0 ; }