| /*- |
| * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org> |
| * based on work by Toshiharu OHNO <tony-o@iij.ad.jp> |
| * Internet Initiative Japan, Inc (IIJ) |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * |
| * $FreeBSD: src/usr.sbin/ppp/command.c,v 1.307.12.1 2010/12/21 17:10:29 kensmith Exp $ |
| */ |
| |
| #include <sys/param.h> |
| #include <netinet/in_systm.h> |
| #include <netinet/in.h> |
| #include <netinet/ip.h> |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| #include <net/route.h> |
| #include <netdb.h> |
| #include <sys/un.h> |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <paths.h> |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/wait.h> |
| #include <termios.h> |
| #include <unistd.h> |
| |
| #ifndef NONAT |
| #ifdef LOCALNAT |
| #include "alias.h" |
| #else |
| #include <alias.h> |
| #endif |
| #endif |
| |
| #include "layer.h" |
| #include "defs.h" |
| #include "command.h" |
| #include "mbuf.h" |
| #include "log.h" |
| #include "timer.h" |
| #include "fsm.h" |
| #include "iplist.h" |
| #include "throughput.h" |
| #include "slcompress.h" |
| #include "lqr.h" |
| #include "hdlc.h" |
| #include "lcp.h" |
| #include "ncpaddr.h" |
| #include "ipcp.h" |
| #ifndef NONAT |
| #include "nat_cmd.h" |
| #endif |
| #include "systems.h" |
| #include "filter.h" |
| #include "descriptor.h" |
| #include "main.h" |
| #include "route.h" |
| #include "ccp.h" |
| #include "auth.h" |
| #include "async.h" |
| #include "link.h" |
| #include "physical.h" |
| #include "mp.h" |
| #ifndef NORADIUS |
| #include "radius.h" |
| #endif |
| #include "ipv6cp.h" |
| #include "ncp.h" |
| #include "bundle.h" |
| #include "server.h" |
| #include "prompt.h" |
| #include "chat.h" |
| #include "chap.h" |
| #include "cbcp.h" |
| #include "datalink.h" |
| #include "iface.h" |
| #include "id.h" |
| #include "probe.h" |
| |
| /* ``set'' values */ |
| #define VAR_AUTHKEY 0 |
| #define VAR_DIAL 1 |
| #define VAR_LOGIN 2 |
| #define VAR_AUTHNAME 3 |
| #define VAR_AUTOLOAD 4 |
| #define VAR_WINSIZE 5 |
| #define VAR_DEVICE 6 |
| #define VAR_ACCMAP 7 |
| #define VAR_MRRU 8 |
| #define VAR_MRU 9 |
| #define VAR_MTU 10 |
| #define VAR_OPENMODE 11 |
| #define VAR_PHONE 12 |
| #define VAR_HANGUP 13 |
| #define VAR_IDLETIMEOUT 14 |
| #define VAR_LQRPERIOD 15 |
| #define VAR_LCPRETRY 16 |
| #define VAR_CHAPRETRY 17 |
| #define VAR_PAPRETRY 18 |
| #define VAR_CCPRETRY 19 |
| #define VAR_IPCPRETRY 20 |
| #define VAR_DNS 21 |
| #define VAR_NBNS 22 |
| #define VAR_MODE 23 |
| #define VAR_CALLBACK 24 |
| #define VAR_CBCP 25 |
| #define VAR_CHOKED 26 |
| #define VAR_SENDPIPE 27 |
| #define VAR_RECVPIPE 28 |
| #define VAR_RADIUS 29 |
| #define VAR_CD 30 |
| #define VAR_PARITY 31 |
| #define VAR_CRTSCTS 32 |
| #define VAR_URGENTPORTS 33 |
| #define VAR_LOGOUT 34 |
| #define VAR_IFQUEUE 35 |
| #define VAR_MPPE 36 |
| #define VAR_IPV6CPRETRY 37 |
| #define VAR_RAD_ALIVE 38 |
| #define VAR_PPPOE 39 |
| #define VAR_PORT_ID 40 |
| |
| /* ``accept|deny|disable|enable'' masks */ |
| #define NEG_HISMASK (1) |
| #define NEG_MYMASK (2) |
| |
| /* ``accept|deny|disable|enable'' values */ |
| #define NEG_ACFCOMP 40 |
| #define NEG_CHAP05 41 |
| #define NEG_CHAP80 42 |
| #define NEG_CHAP80LM 43 |
| #define NEG_DEFLATE 44 |
| #define NEG_DNS 45 |
| #define NEG_ECHO 46 |
| #define NEG_ENDDISC 47 |
| #define NEG_LQR 48 |
| #define NEG_PAP 49 |
| #define NEG_PPPDDEFLATE 50 |
| #define NEG_PRED1 51 |
| #define NEG_PROTOCOMP 52 |
| #define NEG_SHORTSEQ 53 |
| #define NEG_VJCOMP 54 |
| #define NEG_MPPE 55 |
| #define NEG_CHAP81 56 |
| |
| const char Version[] = "3.4.2"; |
| |
| static int ShowCommand(struct cmdargs const *); |
| static int TerminalCommand(struct cmdargs const *); |
| static int QuitCommand(struct cmdargs const *); |
| static int OpenCommand(struct cmdargs const *); |
| static int CloseCommand(struct cmdargs const *); |
| static int DownCommand(struct cmdargs const *); |
| static int SetCommand(struct cmdargs const *); |
| static int LinkCommand(struct cmdargs const *); |
| static int AddCommand(struct cmdargs const *); |
| static int DeleteCommand(struct cmdargs const *); |
| static int NegotiateCommand(struct cmdargs const *); |
| static int ClearCommand(struct cmdargs const *); |
| static int RunListCommand(struct cmdargs const *); |
| static int IfaceAddCommand(struct cmdargs const *); |
| static int IfaceDeleteCommand(struct cmdargs const *); |
| static int IfaceClearCommand(struct cmdargs const *); |
| static int SetProcTitle(struct cmdargs const *); |
| #ifndef NONAT |
| static int NatEnable(struct cmdargs const *); |
| static int NatOption(struct cmdargs const *); |
| #endif |
| |
| static const char * |
| showcx(struct cmdtab const *cmd) |
| { |
| if (cmd->lauth & LOCAL_CX) |
| return "(c)"; |
| else if (cmd->lauth & LOCAL_CX_OPT) |
| return "(o)"; |
| |
| return ""; |
| } |
| |
| static int |
| HelpCommand(struct cmdargs const *arg) |
| { |
| struct cmdtab const *cmd; |
| int n, cmax, dmax, cols, cxlen; |
| const char *cx; |
| |
| if (!arg->prompt) { |
| log_Printf(LogWARN, "help: Cannot help without a prompt\n"); |
| return 0; |
| } |
| |
| if (arg->argc > arg->argn) { |
| for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++) |
| if ((cmd->lauth & arg->prompt->auth) && |
| ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) || |
| (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) { |
| prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd)); |
| return 0; |
| } |
| return -1; |
| } |
| |
| cmax = dmax = 0; |
| for (cmd = arg->cmdtab; cmd->func; cmd++) |
| if (cmd->name && (cmd->lauth & arg->prompt->auth)) { |
| if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax) |
| cmax = n; |
| if ((n = strlen(cmd->helpmes)) > dmax) |
| dmax = n; |
| } |
| |
| cols = 80 / (dmax + cmax + 3); |
| n = 0; |
| prompt_Printf(arg->prompt, "(o) = Optional context," |
| " (c) = Context required\n"); |
| for (cmd = arg->cmdtab; cmd->func; cmd++) |
| if (cmd->name && (cmd->lauth & arg->prompt->auth)) { |
| cx = showcx(cmd); |
| cxlen = cmax - strlen(cmd->name); |
| if (n % cols != 0) |
| prompt_Printf(arg->prompt, " "); |
| prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s", |
| cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes); |
| if (++n % cols == 0) |
| prompt_Printf(arg->prompt, "\n"); |
| } |
| if (n % cols != 0) |
| prompt_Printf(arg->prompt, "\n"); |
| |
| return 0; |
| } |
| |
| static int |
| IdentCommand(struct cmdargs const *arg) |
| { |
| Concatinate(arg->cx->physical->link.lcp.cfg.ident, |
| sizeof arg->cx->physical->link.lcp.cfg.ident, |
| arg->argc - arg->argn, arg->argv + arg->argn); |
| return 0; |
| } |
| |
| static int |
| SendIdentification(struct cmdargs const *arg) |
| { |
| if (arg->cx->state < DATALINK_LCP) { |
| log_Printf(LogWARN, "sendident: link has not reached LCP\n"); |
| return 2; |
| } |
| return lcp_SendIdentification(&arg->cx->physical->link.lcp) ? 0 : 1; |
| } |
| |
| static int |
| CloneCommand(struct cmdargs const *arg) |
| { |
| char namelist[LINE_LEN]; |
| char *name; |
| int f; |
| |
| if (arg->argc == arg->argn) |
| return -1; |
| |
| namelist[sizeof namelist - 1] = '\0'; |
| for (f = arg->argn; f < arg->argc; f++) { |
| strncpy(namelist, arg->argv[f], sizeof namelist - 1); |
| for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) |
| bundle_DatalinkClone(arg->bundle, arg->cx, name); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| RemoveCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc != arg->argn) |
| return -1; |
| |
| if (arg->cx->state != DATALINK_CLOSED) { |
| log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n"); |
| return 2; |
| } |
| |
| bundle_DatalinkRemove(arg->bundle, arg->cx); |
| return 0; |
| } |
| |
| static int |
| RenameCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc != arg->argn + 1) |
| return -1; |
| |
| if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn])) |
| return 0; |
| |
| log_Printf(LogWARN, "%s -> %s: target name already exists\n", |
| arg->cx->name, arg->argv[arg->argn]); |
| return 1; |
| } |
| |
| static int |
| LoadCommand(struct cmdargs const *arg) |
| { |
| const char *err; |
| int n, mode; |
| |
| mode = arg->bundle->phys_type.all; |
| |
| if (arg->argn < arg->argc) { |
| for (n = arg->argn; n < arg->argc; n++) |
| if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) { |
| log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err); |
| return 1; |
| } |
| |
| for (n = arg->argn; n < arg->argc; n++) { |
| bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]); |
| system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx); |
| } |
| bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]); |
| } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) { |
| log_Printf(LogWARN, "default: %s\n", err); |
| return 1; |
| } else { |
| bundle_SetLabel(arg->bundle, "default"); |
| system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx); |
| bundle_SetLabel(arg->bundle, "default"); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| LogCommand(struct cmdargs const *arg) |
| { |
| char buf[LINE_LEN]; |
| |
| if (arg->argn < arg->argc) { |
| char *argv[MAXARGS]; |
| int argc = arg->argc - arg->argn; |
| |
| if (argc >= (int)(sizeof argv / sizeof argv[0])) { |
| argc = sizeof argv / sizeof argv[0] - 1; |
| log_Printf(LogWARN, "Truncating log command to %d args\n", argc); |
| } |
| command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid()); |
| Concatinate(buf, sizeof buf, argc, (const char *const *)argv); |
| log_Printf(LogLOG, "%s\n", buf); |
| command_Free(argc, argv); |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| static int |
| SaveCommand(struct cmdargs const *arg __unused) |
| { |
| log_Printf(LogWARN, "save command is not yet implemented.\n"); |
| return 1; |
| } |
| |
| static int |
| DialCommand(struct cmdargs const *arg) |
| { |
| int res; |
| |
| if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO))) |
| || (!arg->cx && |
| (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) { |
| log_Printf(LogWARN, "Manual dial is only available for auto and" |
| " interactive links\n"); |
| return 1; |
| } |
| |
| if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0) |
| return res; |
| |
| bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1); |
| |
| return 0; |
| } |
| |
| #define isinword(ch) (isalnum(ch) || (ch) == '_') |
| |
| static char * |
| strstrword(char *big, const char *little) |
| { |
| /* Get the first occurance of the word ``little'' in ``big'' */ |
| char *pos; |
| int len; |
| |
| pos = big; |
| len = strlen(little); |
| |
| while ((pos = strstr(pos, little)) != NULL) |
| if ((pos != big && isinword(pos[-1])) || isinword(pos[len])) |
| pos++; |
| else if (pos != big && pos[-1] == '\\') |
| memmove(pos - 1, pos, strlen(pos) + 1); |
| else |
| break; |
| |
| return pos; |
| } |
| |
| static char * |
| subst(char *tgt, const char *oldstr, const char *newstr) |
| { |
| /* tgt is a malloc()d area... realloc() as necessary */ |
| char *word, *ntgt; |
| int ltgt, loldstr, lnewstr, pos; |
| |
| if ((word = strstrword(tgt, oldstr)) == NULL) |
| return tgt; |
| |
| ltgt = strlen(tgt) + 1; |
| loldstr = strlen(oldstr); |
| lnewstr = strlen(newstr); |
| do { |
| pos = word - tgt; |
| if (loldstr > lnewstr) |
| bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr); |
| if (loldstr != lnewstr) { |
| ntgt = realloc(tgt, ltgt += lnewstr - loldstr); |
| if (ntgt == NULL) |
| break; /* Oh wonderful ! */ |
| word = ntgt + pos; |
| tgt = ntgt; |
| } |
| if (lnewstr > loldstr) |
| bcopy(word + loldstr, word + lnewstr, ltgt - pos - lnewstr); |
| bcopy(newstr, word, lnewstr); |
| } while ((word = strstrword(word, oldstr))); |
| |
| return tgt; |
| } |
| |
| static char * |
| substip(char *tgt, const char *oldstr, struct in_addr ip) |
| { |
| return subst(tgt, oldstr, inet_ntoa(ip)); |
| } |
| |
| static char * |
| substlong(char *tgt, const char *oldstr, long l) |
| { |
| char buf[23]; |
| |
| snprintf(buf, sizeof buf, "%ld", l); |
| |
| return subst(tgt, oldstr, buf); |
| } |
| |
| static char * |
| substull(char *tgt, const char *oldstr, unsigned long long ull) |
| { |
| char buf[21]; |
| |
| snprintf(buf, sizeof buf, "%llu", ull); |
| |
| return subst(tgt, oldstr, buf); |
| } |
| |
| |
| #ifndef NOINET6 |
| static char * |
| substipv6(char *tgt, const char *oldstr, const struct ncpaddr *ip) |
| { |
| return subst(tgt, oldstr, ncpaddr_ntoa(ip)); |
| } |
| |
| #ifndef NORADIUS |
| static char * |
| substipv6prefix(char *tgt, const char *oldstr, const uint8_t *ipv6prefix) |
| { |
| uint8_t ipv6addr[INET6_ADDRSTRLEN]; |
| uint8_t prefix[INET6_ADDRSTRLEN + sizeof("/128") - 1]; |
| |
| if (ipv6prefix) { |
| inet_ntop(AF_INET6, &ipv6prefix[2], ipv6addr, sizeof(ipv6addr)); |
| snprintf(prefix, sizeof(prefix), "%s/%d", ipv6addr, ipv6prefix[1]); |
| } else |
| prefix[0] = '\0'; |
| return subst(tgt, oldstr, prefix); |
| } |
| #endif |
| #endif |
| |
| void |
| command_Expand(char **nargv, int argc, char const *const *oargv, |
| struct bundle *bundle, int inc0, pid_t pid) |
| { |
| int arg, secs; |
| char uptime[20]; |
| unsigned long long oin, oout, pin, pout; |
| |
| if (inc0) |
| arg = 0; /* Start at arg 0 */ |
| else { |
| nargv[0] = strdup(oargv[0]); |
| arg = 1; |
| } |
| |
| secs = bundle_Uptime(bundle); |
| snprintf(uptime, sizeof uptime, "%d:%02d:%02d", |
| secs / 3600, (secs / 60) % 60, secs % 60); |
| oin = bundle->ncp.ipcp.throughput.OctetsIn; |
| oout = bundle->ncp.ipcp.throughput.OctetsOut; |
| pin = bundle->ncp.ipcp.throughput.PacketsIn; |
| pout = bundle->ncp.ipcp.throughput.PacketsOut; |
| #ifndef NOINET6 |
| oin += bundle->ncp.ipv6cp.throughput.OctetsIn; |
| oout += bundle->ncp.ipv6cp.throughput.OctetsOut; |
| pin += bundle->ncp.ipv6cp.throughput.PacketsIn; |
| pout += bundle->ncp.ipv6cp.throughput.PacketsOut; |
| #endif |
| |
| for (; arg < argc; arg++) { |
| nargv[arg] = strdup(oargv[arg]); |
| nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name); |
| nargv[arg] = substip(nargv[arg], "DNS0", bundle->ncp.ipcp.ns.dns[0]); |
| nargv[arg] = substip(nargv[arg], "DNS1", bundle->ncp.ipcp.ns.dns[1]); |
| nargv[arg] = subst(nargv[arg], "ENDDISC", |
| mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class, |
| bundle->ncp.mp.cfg.enddisc.address, |
| bundle->ncp.mp.cfg.enddisc.len)); |
| nargv[arg] = substip(nargv[arg], "HISADDR", bundle->ncp.ipcp.peer_ip); |
| #ifndef NOINET6 |
| nargv[arg] = substipv6(nargv[arg], "HISADDR6", &bundle->ncp.ipv6cp.hisaddr); |
| #endif |
| nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name); |
| nargv[arg] = substull(nargv[arg], "IPOCTETSIN", |
| bundle->ncp.ipcp.throughput.OctetsIn); |
| nargv[arg] = substull(nargv[arg], "IPOCTETSOUT", |
| bundle->ncp.ipcp.throughput.OctetsOut); |
| nargv[arg] = substull(nargv[arg], "IPPACKETSIN", |
| bundle->ncp.ipcp.throughput.PacketsIn); |
| nargv[arg] = substull(nargv[arg], "IPPACKETSOUT", |
| bundle->ncp.ipcp.throughput.PacketsOut); |
| #ifndef NOINET6 |
| nargv[arg] = substull(nargv[arg], "IPV6OCTETSIN", |
| bundle->ncp.ipv6cp.throughput.OctetsIn); |
| nargv[arg] = substull(nargv[arg], "IPV6OCTETSOUT", |
| bundle->ncp.ipv6cp.throughput.OctetsOut); |
| nargv[arg] = substull(nargv[arg], "IPV6PACKETSIN", |
| bundle->ncp.ipv6cp.throughput.PacketsIn); |
| nargv[arg] = substull(nargv[arg], "IPV6PACKETSOUT", |
| bundle->ncp.ipv6cp.throughput.PacketsOut); |
| #endif |
| nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle)); |
| nargv[arg] = substip(nargv[arg], "MYADDR", bundle->ncp.ipcp.my_ip); |
| #ifndef NOINET6 |
| nargv[arg] = substipv6(nargv[arg], "MYADDR6", &bundle->ncp.ipv6cp.myaddr); |
| #ifndef NORADIUS |
| nargv[arg] = substipv6prefix(nargv[arg], "IPV6PREFIX", |
| bundle->radius.ipv6prefix); |
| #endif |
| #endif |
| nargv[arg] = substull(nargv[arg], "OCTETSIN", oin); |
| nargv[arg] = substull(nargv[arg], "OCTETSOUT", oout); |
| nargv[arg] = substull(nargv[arg], "PACKETSIN", pin); |
| nargv[arg] = substull(nargv[arg], "PACKETSOUT", pout); |
| nargv[arg] = subst(nargv[arg], "PEER_ENDDISC", |
| mp_Enddisc(bundle->ncp.mp.peer.enddisc.class, |
| bundle->ncp.mp.peer.enddisc.address, |
| bundle->ncp.mp.peer.enddisc.len)); |
| nargv[arg] = substlong(nargv[arg], "PROCESSID", pid); |
| if (server.cfg.port) |
| nargv[arg] = substlong(nargv[arg], "SOCKNAME", server.cfg.port); |
| else |
| nargv[arg] = subst(nargv[arg], "SOCKNAME", server.cfg.sockname); |
| nargv[arg] = subst(nargv[arg], "UPTIME", uptime); |
| nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname); |
| nargv[arg] = subst(nargv[arg], "VERSION", Version); |
| } |
| nargv[arg] = NULL; |
| } |
| |
| void |
| command_Free(int argc, char **argv) |
| { |
| while (argc) { |
| free(*argv); |
| argc--; |
| argv++; |
| } |
| } |
| |
| static int |
| ShellCommand(struct cmdargs const *arg, int bg) |
| { |
| const char *shell; |
| pid_t shpid, pid; |
| |
| #ifdef SHELL_ONLY_INTERACTIVELY |
| /* we're only allowed to shell when we run ppp interactively */ |
| if (arg->prompt && arg->prompt->owner) { |
| log_Printf(LogWARN, "Can't start a shell from a network connection\n"); |
| return 1; |
| } |
| #endif |
| |
| if (arg->argc == arg->argn) { |
| if (!arg->prompt) { |
| log_Printf(LogWARN, "Can't start an interactive shell from" |
| " a config file\n"); |
| return 1; |
| } else if (arg->prompt->owner) { |
| log_Printf(LogWARN, "Can't start an interactive shell from" |
| " a socket connection\n"); |
| return 1; |
| } else if (bg) { |
| log_Printf(LogWARN, "Can only start an interactive shell in" |
| " the foreground mode\n"); |
| return 1; |
| } |
| } |
| |
| pid = getpid(); |
| if ((shpid = fork()) == 0) { |
| int i, fd; |
| |
| if ((shell = getenv("SHELL")) == 0) |
| shell = _PATH_BSHELL; |
| |
| timer_TermService(); |
| |
| if (arg->prompt) |
| fd = arg->prompt->fd_out; |
| else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) { |
| log_Printf(LogALERT, "Failed to open %s: %s\n", |
| _PATH_DEVNULL, strerror(errno)); |
| exit(1); |
| } |
| dup2(fd, STDIN_FILENO); |
| dup2(fd, STDOUT_FILENO); |
| dup2(fd, STDERR_FILENO); |
| for (i = getdtablesize(); i > STDERR_FILENO; i--) |
| fcntl(i, F_SETFD, 1); |
| |
| #ifndef NOSUID |
| setuid(ID0realuid()); |
| #endif |
| if (arg->argc > arg->argn) { |
| /* substitute pseudo args */ |
| char *argv[MAXARGS]; |
| int argc = arg->argc - arg->argn; |
| |
| if (argc >= (int)(sizeof argv / sizeof argv[0])) { |
| argc = sizeof argv / sizeof argv[0] - 1; |
| log_Printf(LogWARN, "Truncating shell command to %d args\n", argc); |
| } |
| command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0, pid); |
| if (bg) { |
| pid_t p; |
| |
| p = getpid(); |
| if (daemon(1, 1) == -1) { |
| log_Printf(LogERROR, "%ld: daemon: %s\n", (long)p, strerror(errno)); |
| exit(1); |
| } |
| } else if (arg->prompt) |
| printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]); |
| execvp(argv[0], argv); |
| } else { |
| if (arg->prompt) |
| printf("ppp: Pausing until %s finishes\n", shell); |
| prompt_TtyOldMode(arg->prompt); |
| execl(shell, shell, (char *)NULL); |
| } |
| |
| log_Printf(LogWARN, "exec() of %s failed: %s\n", |
| arg->argc > arg->argn ? arg->argv[arg->argn] : shell, |
| strerror(errno)); |
| _exit(255); |
| } |
| |
| if (shpid == (pid_t)-1) |
| log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno)); |
| else { |
| int status; |
| waitpid(shpid, &status, 0); |
| } |
| |
| if (arg->prompt && !arg->prompt->owner) |
| prompt_TtyCommandMode(arg->prompt); |
| |
| return 0; |
| } |
| |
| static int |
| BgShellCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn) |
| return -1; |
| return ShellCommand(arg, 1); |
| } |
| |
| static int |
| FgShellCommand(struct cmdargs const *arg) |
| { |
| return ShellCommand(arg, 0); |
| } |
| |
| static int |
| ResolvCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn + 1) { |
| if (!strcasecmp(arg->argv[arg->argn], "reload")) |
| ipcp_LoadDNS(&arg->bundle->ncp.ipcp); |
| else if (!strcasecmp(arg->argv[arg->argn], "restore")) |
| ipcp_RestoreDNS(&arg->bundle->ncp.ipcp); |
| else if (!strcasecmp(arg->argv[arg->argn], "rewrite")) |
| ipcp_WriteDNS(&arg->bundle->ncp.ipcp); |
| else if (!strcasecmp(arg->argv[arg->argn], "readonly")) |
| arg->bundle->ncp.ipcp.ns.writable = 0; |
| else if (!strcasecmp(arg->argv[arg->argn], "writable")) |
| arg->bundle->ncp.ipcp.ns.writable = 1; |
| else |
| return -1; |
| |
| return 0; |
| } |
| |
| return -1; |
| } |
| |
| #ifndef NONAT |
| static struct cmdtab const NatCommands[] = |
| { |
| {"addr", NULL, nat_RedirectAddr, LOCAL_AUTH, |
| "static address translation", "nat addr [addr_local addr_alias]", NULL}, |
| {"deny_incoming", NULL, NatOption, LOCAL_AUTH, |
| "stop incoming connections", "nat deny_incoming yes|no", |
| (const void *) PKT_ALIAS_DENY_INCOMING}, |
| {"enable", NULL, NatEnable, LOCAL_AUTH, |
| "enable NAT", "nat enable yes|no", NULL}, |
| {"log", NULL, NatOption, LOCAL_AUTH, |
| "log NAT link creation", "nat log yes|no", |
| (const void *) PKT_ALIAS_LOG}, |
| {"port", NULL, nat_RedirectPort, LOCAL_AUTH, "port redirection", |
| "nat port proto localaddr:port[-port] aliasport[-aliasport]", NULL}, |
| {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection", |
| "nat proto proto localIP [publicIP [remoteIP]]", NULL}, |
| {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH, |
| "proxy control", "nat proxy server host[:port] ...", NULL}, |
| #ifndef NO_FW_PUNCH |
| {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH, |
| "firewall control", "nat punch_fw [base count]", NULL}, |
| #endif |
| {"skinny_port", NULL, nat_SkinnyPort, LOCAL_AUTH, |
| "TCP port used by Skinny Station protocol", "nat skinny_port [port]", NULL}, |
| {"same_ports", NULL, NatOption, LOCAL_AUTH, |
| "try to leave port numbers unchanged", "nat same_ports yes|no", |
| (const void *) PKT_ALIAS_SAME_PORTS}, |
| {"target", NULL, nat_SetTarget, LOCAL_AUTH, |
| "Default address for incoming connections", "nat target addr", NULL}, |
| {"unregistered_only", NULL, NatOption, LOCAL_AUTH, |
| "translate unregistered (private) IP address space only", |
| "nat unregistered_only yes|no", |
| (const void *) PKT_ALIAS_UNREGISTERED_ONLY}, |
| {"use_sockets", NULL, NatOption, LOCAL_AUTH, |
| "allocate host sockets", "nat use_sockets yes|no", |
| (const void *) PKT_ALIAS_USE_SOCKETS}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "nat help|? [command]", NatCommands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| #endif |
| |
| static struct cmdtab const AllowCommands[] = { |
| {"modes", "mode", AllowModes, LOCAL_AUTH, |
| "Only allow certain ppp modes", "allow modes mode...", NULL}, |
| {"users", "user", AllowUsers, LOCAL_AUTH, |
| "Only allow ppp access to certain users", "allow users logname...", NULL}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "allow help|? [command]", AllowCommands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static struct cmdtab const IfaceCommands[] = |
| { |
| {"add", NULL, IfaceAddCommand, LOCAL_AUTH, |
| "Add iface address", "iface add addr[/bits| mask] peer", NULL}, |
| {NULL, "add!", IfaceAddCommand, LOCAL_AUTH, |
| "Add or change an iface address", "iface add! addr[/bits| mask] peer", |
| (void *)1}, |
| {"clear", NULL, IfaceClearCommand, LOCAL_AUTH, |
| "Clear iface address(es)", "iface clear [INET | INET6]", NULL}, |
| {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH, |
| "Delete iface address", "iface delete addr", NULL}, |
| {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH, |
| "Delete iface address", "iface delete addr", (void *)1}, |
| {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH, |
| "Delete iface address", "iface delete addr", (void *)1}, |
| {"show", NULL, iface_Show, LOCAL_AUTH, |
| "Show iface address(es)", "iface show", NULL}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "nat help|? [command]", IfaceCommands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static struct cmdtab const Commands[] = { |
| {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "accept option request", "accept option ..", NULL}, |
| {"add", NULL, AddCommand, LOCAL_AUTH, |
| "add route", "add dest mask gateway", NULL}, |
| {NULL, "add!", AddCommand, LOCAL_AUTH, |
| "add or change route", "add! dest mask gateway", (void *)1}, |
| {"allow", "auth", RunListCommand, LOCAL_AUTH, |
| "Allow ppp access", "allow users|modes ....", AllowCommands}, |
| {"bg", "!bg", BgShellCommand, LOCAL_AUTH, |
| "Run a background command", "[!]bg command", NULL}, |
| {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Clear throughput statistics", |
| "clear ipcp|ipv6cp|physical [current|overall|peak]...", NULL}, |
| {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX, |
| "Clone a link", "clone newname...", NULL}, |
| {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Close an FSM", "close [lcp|ccp]", NULL}, |
| {"delete", NULL, DeleteCommand, LOCAL_AUTH, |
| "delete route", "delete dest", NULL}, |
| {NULL, "delete!", DeleteCommand, LOCAL_AUTH, |
| "delete a route if it exists", "delete! dest", (void *)1}, |
| {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Deny option request", "deny option ..", NULL}, |
| {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Dial and login", "dial|call [system ...]", NULL}, |
| {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Disable option", "disable option ..", NULL}, |
| {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Generate a down event", "down [ccp|lcp]", NULL}, |
| {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Enable option", "enable option ..", NULL}, |
| {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX, |
| "Set the link identity", "ident text...", NULL}, |
| {"iface", "interface", RunListCommand, LOCAL_AUTH, |
| "interface control", "iface option ...", IfaceCommands}, |
| {"link", "datalink", LinkCommand, LOCAL_AUTH, |
| "Link specific commands", "link name command ...", NULL}, |
| {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Load settings", "load [system ...]", NULL}, |
| {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "log information", "log word ...", NULL}, |
| #ifndef NONAT |
| {"nat", "alias", RunListCommand, LOCAL_AUTH, |
| "NAT control", "nat option yes|no", NatCommands}, |
| #endif |
| {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1}, |
| {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH, |
| "Password for manipulation", "passwd LocalPassword", NULL}, |
| {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Quit PPP program", "quit|bye [all]", NULL}, |
| {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX, |
| "Remove a link", "remove", NULL}, |
| {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX, |
| "Rename a link", "rename name", NULL}, |
| {"resolv", NULL, ResolvCommand, LOCAL_AUTH, |
| "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable", |
| NULL}, |
| {"save", NULL, SaveCommand, LOCAL_AUTH, |
| "Save settings", "save", NULL}, |
| {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX, |
| "Transmit the link identity", "sendident", NULL}, |
| {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Set parameters", "set[up] var value", NULL}, |
| {"shell", "!", FgShellCommand, LOCAL_AUTH, |
| "Run a subshell", "shell|! [sh command]", NULL}, |
| {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Show status and stats", "show var", NULL}, |
| {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX, |
| "Enter terminal mode", "term", NULL}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "help|? [command]", Commands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static int |
| ShowEscape(struct cmdargs const *arg) |
| { |
| if (arg->cx->physical->async.cfg.EscMap[32]) { |
| int code, bit; |
| const char *sep = ""; |
| |
| for (code = 0; code < 32; code++) |
| if (arg->cx->physical->async.cfg.EscMap[code]) |
| for (bit = 0; bit < 8; bit++) |
| if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) { |
| prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit); |
| sep = ", "; |
| } |
| prompt_Printf(arg->prompt, "\n"); |
| } |
| return 0; |
| } |
| |
| static int |
| ShowTimerList(struct cmdargs const *arg) |
| { |
| timer_Show(0, arg->prompt); |
| return 0; |
| } |
| |
| static int |
| ShowStopped(struct cmdargs const *arg) |
| { |
| prompt_Printf(arg->prompt, " Stopped Timer: LCP: "); |
| if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load) |
| prompt_Printf(arg->prompt, "Disabled"); |
| else |
| prompt_Printf(arg->prompt, "%ld secs", |
| arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS); |
| |
| prompt_Printf(arg->prompt, ", CCP: "); |
| if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load) |
| prompt_Printf(arg->prompt, "Disabled"); |
| else |
| prompt_Printf(arg->prompt, "%ld secs", |
| arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS); |
| |
| prompt_Printf(arg->prompt, "\n"); |
| |
| return 0; |
| } |
| |
| static int |
| ShowVersion(struct cmdargs const *arg) |
| { |
| prompt_Printf(arg->prompt, "PPP Version %s\n", Version); |
| return 0; |
| } |
| |
| static int |
| ShowProtocolStats(struct cmdargs const *arg) |
| { |
| struct link *l = command_ChooseLink(arg); |
| |
| prompt_Printf(arg->prompt, "%s:\n", l->name); |
| link_ReportProtocolStatus(l, arg->prompt); |
| return 0; |
| } |
| |
| static struct cmdtab const ShowCommands[] = { |
| {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH, |
| "bundle details", "show bundle", NULL}, |
| {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT, |
| "CCP status", "show cpp", NULL}, |
| {"compress", NULL, sl_Show, LOCAL_AUTH, |
| "VJ compression stats", "show compress", NULL}, |
| {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX, |
| "escape characters", "show escape", NULL}, |
| {"filter", NULL, filter_Show, LOCAL_AUTH, |
| "packet filters", "show filter [in|out|dial|alive]", NULL}, |
| {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX, |
| "HDLC errors", "show hdlc", NULL}, |
| {"iface", "interface", iface_Show, LOCAL_AUTH, |
| "Interface status", "show iface", NULL}, |
| {"ipcp", NULL, ipcp_Show, LOCAL_AUTH, |
| "IPCP status", "show ipcp", NULL}, |
| #ifndef NOINET6 |
| {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH, |
| "IPV6CP status", "show ipv6cp", NULL}, |
| #endif |
| {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Protocol layers", "show layers", NULL}, |
| {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX, |
| "LCP status", "show lcp", NULL}, |
| {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX, |
| "(high-level) link info", "show link", NULL}, |
| {"links", NULL, bundle_ShowLinks, LOCAL_AUTH, |
| "available link names", "show links", NULL}, |
| {"log", NULL, log_ShowLevel, LOCAL_AUTH, |
| "log levels", "show log", NULL}, |
| {"mem", NULL, mbuf_Show, LOCAL_AUTH, |
| "mbuf allocations", "show mem", NULL}, |
| {"ncp", NULL, ncp_Show, LOCAL_AUTH, |
| "NCP status", "show ncp", NULL}, |
| {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX, |
| "(low-level) link info", "show physical", NULL}, |
| {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH, |
| "multilink setup", "show mp", NULL}, |
| {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT, |
| "protocol summary", "show proto", NULL}, |
| {"route", NULL, route_Show, LOCAL_AUTH, |
| "routing table", "show route", NULL}, |
| {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX, |
| "STOPPED timeout", "show stopped", NULL}, |
| {"timers", NULL, ShowTimerList, LOCAL_AUTH, |
| "alarm timers", "show timers", NULL}, |
| {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH, |
| "version string", "show version", NULL}, |
| {"who", NULL, log_ShowWho, LOCAL_AUTH, |
| "client list", "show who", NULL}, |
| {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH, |
| "Display this message", "show help|? [command]", ShowCommands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static struct cmdtab const * |
| FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch) |
| { |
| int nmatch; |
| int len; |
| struct cmdtab const *found; |
| |
| found = NULL; |
| len = strlen(str); |
| nmatch = 0; |
| while (cmds->func) { |
| if (cmds->name && strncasecmp(str, cmds->name, len) == 0) { |
| if (cmds->name[len] == '\0') { |
| *pmatch = 1; |
| return cmds; |
| } |
| nmatch++; |
| found = cmds; |
| } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) { |
| if (cmds->alias[len] == '\0') { |
| *pmatch = 1; |
| return cmds; |
| } |
| nmatch++; |
| found = cmds; |
| } |
| cmds++; |
| } |
| *pmatch = nmatch; |
| return found; |
| } |
| |
| static const char * |
| mkPrefix(int argc, char const *const *argv, char *tgt, int sz) |
| { |
| int f, tlen, len; |
| |
| tlen = 0; |
| for (f = 0; f < argc && tlen < sz - 2; f++) { |
| if (f) |
| tgt[tlen++] = ' '; |
| len = strlen(argv[f]); |
| if (len > sz - tlen - 1) |
| len = sz - tlen - 1; |
| strncpy(tgt+tlen, argv[f], len); |
| tlen += len; |
| } |
| tgt[tlen] = '\0'; |
| return tgt; |
| } |
| |
| static int |
| FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn, |
| char const *const *argv, struct prompt *prompt, struct datalink *cx) |
| { |
| struct cmdtab const *cmd; |
| int val = 1; |
| int nmatch; |
| struct cmdargs arg; |
| char prefix[100]; |
| |
| cmd = FindCommand(cmds, argv[argn], &nmatch); |
| if (nmatch > 1) |
| log_Printf(LogWARN, "%s: Ambiguous command\n", |
| mkPrefix(argn+1, argv, prefix, sizeof prefix)); |
| else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) { |
| if ((cmd->lauth & LOCAL_CX) && !cx) |
| /* We've got no context, but we require it */ |
| cx = bundle2datalink(bundle, NULL); |
| |
| if ((cmd->lauth & LOCAL_CX) && !cx) |
| log_Printf(LogWARN, "%s: No context (use the `link' command)\n", |
| mkPrefix(argn+1, argv, prefix, sizeof prefix)); |
| else { |
| if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) { |
| log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n", |
| mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name); |
| cx = NULL; |
| } |
| arg.cmdtab = cmds; |
| arg.cmd = cmd; |
| arg.argc = argc; |
| arg.argn = argn+1; |
| arg.argv = argv; |
| arg.bundle = bundle; |
| arg.cx = cx; |
| arg.prompt = prompt; |
| val = (*cmd->func) (&arg); |
| } |
| } else |
| log_Printf(LogWARN, "%s: Invalid command\n", |
| mkPrefix(argn+1, argv, prefix, sizeof prefix)); |
| |
| if (val == -1) |
| log_Printf(LogWARN, "usage: %s\n", cmd->syntax); |
| else if (val) |
| log_Printf(LogWARN, "%s: Failed %d\n", |
| mkPrefix(argn+1, argv, prefix, sizeof prefix), val); |
| |
| return val; |
| } |
| |
| int |
| command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset) |
| { |
| char buff2[LINE_LEN-offset]; |
| |
| InterpretArg(buff, buff2); |
| strncpy(buff, buff2, LINE_LEN - offset - 1); |
| buff[LINE_LEN - offset - 1] = '\0'; |
| |
| return command_Interpret(buff, nb, argv); |
| } |
| |
| int |
| command_Interpret(char *buff, int nb, char *argv[MAXARGS]) |
| { |
| char *cp; |
| |
| if (nb > 0) { |
| cp = buff + strcspn(buff, "\r\n"); |
| if (cp) |
| *cp = '\0'; |
| return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE); |
| } |
| return 0; |
| } |
| |
| static int |
| arghidden(char const *const *argv, int n) |
| { |
| /* Is arg n of the given command to be hidden from the log ? */ |
| |
| /* set authkey xxxxx */ |
| /* set key xxxxx */ |
| if (n == 2 && !strncasecmp(argv[0], "se", 2) && |
| (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2))) |
| return 1; |
| |
| /* passwd xxxxx */ |
| if (n == 1 && !strncasecmp(argv[0], "p", 1)) |
| return 1; |
| |
| /* set server port xxxxx .... */ |
| if (n == 3 && !strncasecmp(argv[0], "se", 2) && |
| !strncasecmp(argv[1], "se", 2)) |
| return 1; |
| |
| return 0; |
| } |
| |
| void |
| command_Run(struct bundle *bundle, int argc, char const *const *argv, |
| struct prompt *prompt, const char *label, struct datalink *cx) |
| { |
| if (argc > 0) { |
| if (log_IsKept(LogCOMMAND)) { |
| char buf[LINE_LEN]; |
| int f; |
| size_t n; |
| |
| if (label) { |
| strncpy(buf, label, sizeof buf - 3); |
| buf[sizeof buf - 3] = '\0'; |
| strcat(buf, ": "); |
| n = strlen(buf); |
| } else { |
| *buf = '\0'; |
| n = 0; |
| } |
| buf[sizeof buf - 1] = '\0'; /* In case we run out of room in buf */ |
| |
| for (f = 0; f < argc; f++) { |
| if (n < sizeof buf - 1 && f) |
| buf[n++] = ' '; |
| if (arghidden(argv, f)) |
| strncpy(buf+n, "********", sizeof buf - n - 1); |
| else |
| strncpy(buf+n, argv[f], sizeof buf - n - 1); |
| n += strlen(buf+n); |
| } |
| log_Printf(LogCOMMAND, "%s\n", buf); |
| } |
| FindExec(bundle, Commands, argc, 0, argv, prompt, cx); |
| } |
| } |
| |
| int |
| command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt, |
| const char *label) |
| { |
| int argc; |
| char *argv[MAXARGS]; |
| |
| if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0) |
| return 0; |
| |
| command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL); |
| return 1; |
| } |
| |
| static int |
| ShowCommand(struct cmdargs const *arg) |
| { |
| if (!arg->prompt) |
| log_Printf(LogWARN, "show: Cannot show without a prompt\n"); |
| else if (arg->argc > arg->argn) |
| FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv, |
| arg->prompt, arg->cx); |
| else |
| prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n"); |
| |
| return 0; |
| } |
| |
| static int |
| TerminalCommand(struct cmdargs const *arg) |
| { |
| if (!arg->prompt) { |
| log_Printf(LogWARN, "term: Need a prompt\n"); |
| return 1; |
| } |
| |
| if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) { |
| prompt_Printf(arg->prompt, "LCP state is [%s]\n", |
| State2Nam(arg->cx->physical->link.lcp.fsm.state)); |
| return 1; |
| } |
| |
| datalink_Up(arg->cx, 0, 0); |
| prompt_TtyTermMode(arg->prompt, arg->cx); |
| return 0; |
| } |
| |
| static int |
| QuitCommand(struct cmdargs const *arg) |
| { |
| if (!arg->prompt || prompt_IsController(arg->prompt) || |
| (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") && |
| (arg->prompt->auth & LOCAL_AUTH))) |
| Cleanup(); |
| if (arg->prompt) |
| prompt_Destroy(arg->prompt, 1); |
| |
| return 0; |
| } |
| |
| static int |
| OpenCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn) |
| bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1); |
| else if (arg->argc == arg->argn + 1) { |
| if (!strcasecmp(arg->argv[arg->argn], "lcp")) { |
| struct datalink *cx = arg->cx ? |
| arg->cx : bundle2datalink(arg->bundle, NULL); |
| if (cx) { |
| if (cx->physical->link.lcp.fsm.state == ST_OPENED) |
| fsm_Reopen(&cx->physical->link.lcp.fsm); |
| else |
| bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1); |
| } else |
| log_Printf(LogWARN, "open lcp: You must specify a link\n"); |
| } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) { |
| struct fsm *fp; |
| |
| fp = &command_ChooseLink(arg)->ccp.fsm; |
| if (fp->link->lcp.fsm.state != ST_OPENED) |
| log_Printf(LogWARN, "open: LCP must be open before opening CCP\n"); |
| else if (fp->state == ST_OPENED) |
| fsm_Reopen(fp); |
| else { |
| fp->open_mode = 0; /* Not passive any more */ |
| if (fp->state == ST_STOPPED) { |
| fsm_Down(fp); |
| fsm_Up(fp); |
| } else { |
| fsm_Up(fp); |
| fsm_Open(fp); |
| } |
| } |
| } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) { |
| if (arg->cx) |
| log_Printf(LogWARN, "open ipcp: You need not specify a link\n"); |
| if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) |
| fsm_Reopen(&arg->bundle->ncp.ipcp.fsm); |
| else |
| bundle_Open(arg->bundle, NULL, PHYS_ALL, 1); |
| } else |
| return -1; |
| } else |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| CloseCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn) |
| bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN); |
| else if (arg->argc == arg->argn + 1) { |
| if (!strcasecmp(arg->argv[arg->argn], "lcp")) |
| bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP); |
| else if (!strcasecmp(arg->argv[arg->argn], "ccp") || |
| !strcasecmp(arg->argv[arg->argn], "ccp!")) { |
| struct fsm *fp; |
| |
| fp = &command_ChooseLink(arg)->ccp.fsm; |
| if (fp->state == ST_OPENED) { |
| fsm_Close(fp); |
| if (arg->argv[arg->argn][3] == '!') |
| fp->open_mode = 0; /* Stay ST_CLOSED */ |
| else |
| fp->open_mode = OPEN_PASSIVE; /* Wait for the peer to start */ |
| } |
| } else |
| return -1; |
| } else |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| DownCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn) { |
| if (arg->cx) |
| datalink_Down(arg->cx, CLOSE_STAYDOWN); |
| else |
| bundle_Down(arg->bundle, CLOSE_STAYDOWN); |
| } else if (arg->argc == arg->argn + 1) { |
| if (!strcasecmp(arg->argv[arg->argn], "lcp")) { |
| if (arg->cx) |
| datalink_Down(arg->cx, CLOSE_LCP); |
| else |
| bundle_Down(arg->bundle, CLOSE_LCP); |
| } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) { |
| struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm : |
| &arg->bundle->ncp.mp.link.ccp.fsm; |
| fsm2initial(fp); |
| } else |
| return -1; |
| } else |
| return -1; |
| |
| return 0; |
| } |
| |
| static int |
| SetModemSpeed(struct cmdargs const *arg) |
| { |
| long speed; |
| char *end; |
| |
| if (arg->argc > arg->argn && *arg->argv[arg->argn]) { |
| if (arg->argc > arg->argn+1) { |
| log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n"); |
| return -1; |
| } |
| if (strcasecmp(arg->argv[arg->argn], "sync") == 0) { |
| physical_SetSync(arg->cx->physical); |
| return 0; |
| } |
| end = NULL; |
| speed = strtol(arg->argv[arg->argn], &end, 10); |
| if (*end || speed < 0) { |
| log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"", |
| arg->argv[arg->argn]); |
| return -1; |
| } |
| if (physical_SetSpeed(arg->cx->physical, speed)) |
| return 0; |
| log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]); |
| } else |
| log_Printf(LogWARN, "SetModemSpeed: No speed specified\n"); |
| |
| return -1; |
| } |
| |
| static int |
| SetStoppedTimeout(struct cmdargs const *arg) |
| { |
| struct link *l = &arg->cx->physical->link; |
| |
| l->lcp.fsm.StoppedTimer.load = 0; |
| l->ccp.fsm.StoppedTimer.load = 0; |
| if (arg->argc <= arg->argn+2) { |
| if (arg->argc > arg->argn) { |
| l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS; |
| if (arg->argc > arg->argn+1) |
| l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS; |
| } |
| return 0; |
| } |
| return -1; |
| } |
| |
| static int |
| SetServer(struct cmdargs const *arg) |
| { |
| int res = -1; |
| |
| if (arg->argc > arg->argn && arg->argc < arg->argn+4) { |
| const char *port, *passwd, *mask; |
| size_t mlen; |
| |
| /* What's what ? */ |
| port = arg->argv[arg->argn]; |
| if (arg->argc == arg->argn + 2) { |
| passwd = arg->argv[arg->argn+1]; |
| mask = NULL; |
| } else if (arg->argc == arg->argn + 3) { |
| passwd = arg->argv[arg->argn+1]; |
| mask = arg->argv[arg->argn+2]; |
| mlen = strlen(mask); |
| if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen || |
| (mlen == 4 && *mask != '0')) { |
| log_Printf(LogWARN, "%s %s: %s: Invalid mask\n", |
| arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask); |
| return -1; |
| } |
| } else if (arg->argc != arg->argn + 1) |
| return -1; |
| else if (strcasecmp(port, "none") == 0) { |
| if (server_Clear(arg->bundle)) |
| log_Printf(LogPHASE, "Disabled server socket\n"); |
| return 0; |
| } else if (strcasecmp(port, "open") == 0) { |
| switch (server_Reopen(arg->bundle)) { |
| case SERVER_OK: |
| return 0; |
| case SERVER_FAILED: |
| log_Printf(LogWARN, "Failed to reopen server port\n"); |
| return 1; |
| case SERVER_UNSET: |
| log_Printf(LogWARN, "Cannot reopen unset server socket\n"); |
| return 1; |
| default: |
| break; |
| } |
| return -1; |
| } else if (strcasecmp(port, "closed") == 0) { |
| if (server_Close(arg->bundle)) |
| log_Printf(LogPHASE, "Closed server socket\n"); |
| else |
| log_Printf(LogWARN, "Server socket not open\n"); |
| |
| return 0; |
| } else |
| return -1; |
| |
| strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1); |
| server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0'; |
| |
| if (*port == '/') { |
| mode_t imask; |
| char *ptr, name[LINE_LEN + 12]; |
| |
| if (mask == NULL) |
| imask = (mode_t)-1; |
| else for (imask = mlen = 0; mask[mlen]; mlen++) |
| imask = (imask * 8) + mask[mlen] - '0'; |
| |
| ptr = strstr(port, "%d"); |
| if (ptr) { |
| snprintf(name, sizeof name, "%.*s%d%s", |
| (int)(ptr - port), port, arg->bundle->unit, ptr + 2); |
| port = name; |
| } |
| res = server_LocalOpen(arg->bundle, port, imask); |
| } else { |
| int iport, add = 0; |
| |
| if (mask != NULL) |
| return -1; |
| |
| if (*port == '+') { |
| port++; |
| add = 1; |
| } |
| if (strspn(port, "0123456789") != strlen(port)) { |
| struct servent *s; |
| |
| if ((s = getservbyname(port, "tcp")) == NULL) { |
| iport = 0; |
| log_Printf(LogWARN, "%s: Invalid port or service\n", port); |
| } else |
| iport = ntohs(s->s_port); |
| } else |
| iport = atoi(port); |
| |
| if (iport) { |
| if (add) |
| iport += arg->bundle->unit; |
| res = server_TcpOpen(arg->bundle, iport); |
| } else |
| res = -1; |
| } |
| } |
| |
| return res; |
| } |
| |
| static int |
| SetEscape(struct cmdargs const *arg) |
| { |
| int code; |
| int argc = arg->argc - arg->argn; |
| char const *const *argv = arg->argv + arg->argn; |
| |
| for (code = 0; code < 33; code++) |
| arg->cx->physical->async.cfg.EscMap[code] = 0; |
| |
| while (argc-- > 0) { |
| sscanf(*argv++, "%x", &code); |
| code &= 0xff; |
| arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7)); |
| arg->cx->physical->async.cfg.EscMap[32] = 1; |
| } |
| return 0; |
| } |
| |
| static int |
| SetInterfaceAddr(struct cmdargs const *arg) |
| { |
| struct ncp *ncp = &arg->bundle->ncp; |
| struct ncpaddr ncpaddr; |
| const char *hisaddr; |
| |
| if (arg->argc > arg->argn + 4) |
| return -1; |
| |
| hisaddr = NULL; |
| memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range); |
| memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range); |
| ncp->ipcp.cfg.HaveTriggerAddress = 0; |
| ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY; |
| iplist_reset(&ncp->ipcp.cfg.peer_list); |
| |
| if (arg->argc > arg->argn) { |
| if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn])) |
| return 1; |
| if (arg->argc > arg->argn+1) { |
| hisaddr = arg->argv[arg->argn+1]; |
| if (arg->argc > arg->argn+2) { |
| ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask = |
| GetIpAddr(arg->argv[arg->argn+2]); |
| if (arg->argc > arg->argn+3) { |
| ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]); |
| ncp->ipcp.cfg.HaveTriggerAddress = 1; |
| } |
| } |
| } |
| } |
| |
| /* 0.0.0.0 means any address (0 bits) */ |
| ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr); |
| ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip); |
| if (ncp->ipcp.my_ip.s_addr == INADDR_ANY) |
| ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0); |
| bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL); |
| |
| if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr, |
| arg->bundle->phys_type.all & PHYS_AUTO)) |
| return 4; |
| |
| return 0; |
| } |
| |
| static int |
| SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq, |
| u_int *maxtrm, int def) |
| { |
| if (argc == 0) { |
| *timeout = DEF_FSMRETRY; |
| *maxreq = def; |
| if (maxtrm != NULL) |
| *maxtrm = def; |
| } else { |
| long l = atol(argv[0]); |
| |
| if (l < MIN_FSMRETRY) { |
| log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n", |
| l, MIN_FSMRETRY); |
| return 1; |
| } else |
| *timeout = l; |
| |
| if (argc > 1) { |
| l = atol(argv[1]); |
| if (l < 1) { |
| log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l); |
| l = 1; |
| } |
| *maxreq = l; |
| |
| if (argc > 2 && maxtrm != NULL) { |
| l = atol(argv[2]); |
| if (l < 1) { |
| log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l); |
| l = 1; |
| } |
| *maxtrm = l; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| SetVariable(struct cmdargs const *arg) |
| { |
| long long_val, param = (long)arg->cmd->args; |
| int mode, dummyint, f, first, res; |
| u_short *change; |
| const char *argp; |
| struct datalink *cx = arg->cx; /* LOCAL_CX uses this */ |
| struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */ |
| struct in_addr *ipaddr; |
| struct ncpaddr ncpaddr[2]; |
| |
| if (arg->argc > arg->argn) |
| argp = arg->argv[arg->argn]; |
| else |
| argp = ""; |
| |
| res = 0; |
| |
| if ((arg->cmd->lauth & LOCAL_CX) && !cx) { |
| log_Printf(LogWARN, "set %s: No context (use the `link' command)\n", |
| arg->cmd->name); |
| return 1; |
| } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) { |
| log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n", |
| arg->cmd->name, cx->name); |
| cx = NULL; |
| } |
| |
| switch (param) { |
| case VAR_AUTHKEY: |
| strncpy(arg->bundle->cfg.auth.key, argp, |
| sizeof arg->bundle->cfg.auth.key - 1); |
| arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0'; |
| break; |
| |
| case VAR_AUTHNAME: |
| switch (bundle_Phase(arg->bundle)) { |
| default: |
| log_Printf(LogWARN, "Altering authname while at phase %s\n", |
| bundle_PhaseName(arg->bundle)); |
| /* drop through */ |
| case PHASE_DEAD: |
| case PHASE_ESTABLISH: |
| strncpy(arg->bundle->cfg.auth.name, argp, |
| sizeof arg->bundle->cfg.auth.name - 1); |
| arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0'; |
| break; |
| } |
| break; |
| |
| case VAR_AUTOLOAD: |
| if (arg->argc == arg->argn + 3) { |
| int v1, v2, v3; |
| char *end; |
| |
| v1 = strtol(arg->argv[arg->argn], &end, 0); |
| if (v1 < 0 || *end) { |
| log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n", |
| arg->argv[arg->argn]); |
| res = 1; |
| break; |
| } |
| |
| v2 = strtol(arg->argv[arg->argn + 1], &end, 0); |
| if (v2 < 0 || *end) { |
| log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n", |
| arg->argv[arg->argn + 1]); |
| res = 1; |
| break; |
| } |
| if (v2 < v1) { |
| v3 = v1; |
| v1 = v2; |
| v2 = v3; |
| } |
| |
| v3 = strtol(arg->argv[arg->argn + 2], &end, 0); |
| if (v3 <= 0 || *end) { |
| log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n", |
| arg->argv[arg->argn + 2]); |
| res = 1; |
| break; |
| } |
| |
| arg->bundle->ncp.mp.cfg.autoload.min = v1; |
| arg->bundle->ncp.mp.cfg.autoload.max = v2; |
| arg->bundle->ncp.mp.cfg.autoload.period = v3; |
| mp_RestartAutoloadTimer(&arg->bundle->ncp.mp); |
| } else { |
| log_Printf(LogWARN, "Set autoload requires three arguments\n"); |
| res = 1; |
| } |
| break; |
| |
| case VAR_DIAL: |
| strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1); |
| cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0'; |
| break; |
| |
| case VAR_LOGIN: |
| strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1); |
| cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0'; |
| break; |
| |
| case VAR_WINSIZE: |
| if (arg->argc > arg->argn) { |
| l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]); |
| if (l->ccp.cfg.deflate.out.winsize < 8 || |
| l->ccp.cfg.deflate.out.winsize > 15) { |
| log_Printf(LogWARN, "%d: Invalid outgoing window size\n", |
| l->ccp.cfg.deflate.out.winsize); |
| l->ccp.cfg.deflate.out.winsize = 15; |
| } |
| if (arg->argc > arg->argn+1) { |
| l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]); |
| if (l->ccp.cfg.deflate.in.winsize < 8 || |
| l->ccp.cfg.deflate.in.winsize > 15) { |
| log_Printf(LogWARN, "%d: Invalid incoming window size\n", |
| l->ccp.cfg.deflate.in.winsize); |
| l->ccp.cfg.deflate.in.winsize = 15; |
| } |
| } else |
| l->ccp.cfg.deflate.in.winsize = 0; |
| } else { |
| log_Printf(LogWARN, "No window size specified\n"); |
| res = 1; |
| } |
| break; |
| |
| #ifndef NODES |
| case VAR_MPPE: |
| if (arg->argc > arg->argn + 2) { |
| res = -1; |
| break; |
| } |
| |
| if (arg->argc == arg->argn) { |
| l->ccp.cfg.mppe.keybits = 0; |
| l->ccp.cfg.mppe.state = MPPE_ANYSTATE; |
| l->ccp.cfg.mppe.required = 0; |
| break; |
| } |
| |
| if (!strcmp(argp, "*")) |
| long_val = 0; |
| else { |
| long_val = atol(argp); |
| if (long_val != 40 && long_val != 56 && long_val != 128) { |
| log_Printf(LogWARN, "%s: Invalid bits value\n", argp); |
| res = -1; |
| break; |
| } |
| } |
| |
| if (arg->argc == arg->argn + 2) { |
| if (!strcmp(arg->argv[arg->argn + 1], "*")) |
| l->ccp.cfg.mppe.state = MPPE_ANYSTATE; |
| else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless")) |
| l->ccp.cfg.mppe.state = MPPE_STATELESS; |
| else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful")) |
| l->ccp.cfg.mppe.state = MPPE_STATEFUL; |
| else { |
| log_Printf(LogWARN, "%s: Invalid state value\n", |
| arg->argv[arg->argn + 1]); |
| res = -1; |
| break; |
| } |
| } else |
| l->ccp.cfg.mppe.state = MPPE_ANYSTATE; |
| l->ccp.cfg.mppe.keybits = long_val; |
| l->ccp.cfg.mppe.required = 1; |
| break; |
| #endif |
| |
| case VAR_DEVICE: |
| physical_SetDeviceList(cx->physical, arg->argc - arg->argn, |
| arg->argv + arg->argn); |
| break; |
| |
| case VAR_ACCMAP: |
| if (arg->argc > arg->argn) { |
| u_long ulong_val; |
| sscanf(argp, "%lx", &ulong_val); |
| cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val; |
| } else { |
| log_Printf(LogWARN, "No accmap specified\n"); |
| res = 1; |
| } |
| break; |
| |
| case VAR_MODE: |
| mode = Nam2mode(argp); |
| if (mode == PHYS_NONE || mode == PHYS_ALL) { |
| log_Printf(LogWARN, "%s: Invalid mode\n", argp); |
| res = -1; |
| break; |
| } |
| bundle_SetMode(arg->bundle, cx, mode); |
| break; |
| |
| case VAR_MRRU: |
| switch (bundle_Phase(arg->bundle)) { |
| case PHASE_DEAD: |
| break; |
| case PHASE_ESTABLISH: |
| /* Make sure none of our links are DATALINK_LCP or greater */ |
| if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) { |
| log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n"); |
| res = 1; |
| break; |
| } |
| break; |
| default: |
| log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n"); |
| res = 1; |
| break; |
| } |
| if (res != 0) |
| break; |
| long_val = atol(argp); |
| if (long_val && long_val < MIN_MRU) { |
| log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU); |
| res = 1; |
| break; |
| } else if (long_val > MAX_MRU) { |
| log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU); |
| res = 1; |
| break; |
| } else |
| arg->bundle->ncp.mp.cfg.mrru = long_val; |
| break; |
| |
| case VAR_MRU: |
| long_val = 0; /* silence gcc */ |
| change = NULL; /* silence gcc */ |
| switch(arg->argc - arg->argn) { |
| case 1: |
| if (argp[strspn(argp, "0123456789")] != '\0') { |
| res = -1; |
| break; |
| } |
| /*FALLTHRU*/ |
| case 0: |
| long_val = atol(argp); |
| change = &l->lcp.cfg.mru; |
| if (long_val > l->lcp.cfg.max_mru) { |
| log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val, |
| l->lcp.cfg.max_mru); |
| res = 1; |
| break; |
| } |
| break; |
| case 2: |
| if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) { |
| res = -1; |
| break; |
| } |
| long_val = atol(arg->argv[arg->argn + 1]); |
| change = &l->lcp.cfg.max_mru; |
| if (long_val > MAX_MRU) { |
| log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val, |
| MAX_MRU); |
| res = 1; |
| break; |
| } |
| break; |
| default: |
| res = -1; |
| break; |
| } |
| if (res != 0) |
| break; |
| |
| if (long_val == 0) |
| *change = 0; |
| else if (long_val < MIN_MRU) { |
| log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU); |
| res = 1; |
| break; |
| } else if (long_val > MAX_MRU) { |
| log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU); |
| res = 1; |
| break; |
| } else |
| *change = long_val; |
| if (l->lcp.cfg.mru > *change) |
| l->lcp.cfg.mru = *change; |
| break; |
| |
| case VAR_MTU: |
| long_val = 0; /* silence gcc */ |
| change = NULL; /* silence gcc */ |
| switch(arg->argc - arg->argn) { |
| case 1: |
| if (argp[strspn(argp, "0123456789")] != '\0') { |
| res = -1; |
| break; |
| } |
| /*FALLTHRU*/ |
| case 0: |
| long_val = atol(argp); |
| change = &l->lcp.cfg.mtu; |
| if (long_val > l->lcp.cfg.max_mtu) { |
| log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val, |
| l->lcp.cfg.max_mtu); |
| res = 1; |
| break; |
| } |
| break; |
| case 2: |
| if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) { |
| res = -1; |
| break; |
| } |
| long_val = atol(arg->argv[arg->argn + 1]); |
| change = &l->lcp.cfg.max_mtu; |
| if (long_val > MAX_MTU) { |
| log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val, |
| MAX_MTU); |
| res = 1; |
| break; |
| } |
| break; |
| default: |
| res = -1; |
| break; |
| } |
| |
| if (res != 0) |
| break; |
| |
| if (long_val && long_val < MIN_MTU) { |
| log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU); |
| res = 1; |
| break; |
| } else if (long_val > MAX_MTU) { |
| log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU); |
| res = 1; |
| break; |
| } else |
| *change = long_val; |
| if (l->lcp.cfg.mtu > *change) |
| l->lcp.cfg.mtu = *change; |
| break; |
| |
| case VAR_OPENMODE: |
| if (strcasecmp(argp, "active") == 0) |
| cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ? |
| atoi(arg->argv[arg->argn+1]) : 1; |
| else if (strcasecmp(argp, "passive") == 0) |
| cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE; |
| else { |
| log_Printf(LogWARN, "%s: Invalid openmode\n", argp); |
| res = 1; |
| } |
| break; |
| |
| case VAR_PHONE: |
| strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1); |
| cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0'; |
| cx->phone.alt = cx->phone.next = NULL; |
| break; |
| |
| case VAR_HANGUP: |
| strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1); |
| cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0'; |
| break; |
| |
| case VAR_IFQUEUE: |
| long_val = atol(argp); |
| arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val; |
| break; |
| |
| case VAR_LOGOUT: |
| strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1); |
| cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0'; |
| break; |
| |
| case VAR_IDLETIMEOUT: |
| if (arg->argc > arg->argn+2) { |
| log_Printf(LogWARN, "Too many idle timeout values\n"); |
| res = 1; |
| } else if (arg->argc == arg->argn) { |
| log_Printf(LogWARN, "Too few idle timeout values\n"); |
| res = 1; |
| } else { |
| unsigned long timeout, min; |
| |
| timeout = strtoul(argp, NULL, 10); |
| min = arg->bundle->cfg.idle.min_timeout; |
| if (arg->argc == arg->argn + 2) |
| min = strtoul(arg->argv[arg->argn + 1], NULL, 10); |
| bundle_SetIdleTimer(arg->bundle, timeout, min); |
| } |
| break; |
| |
| #ifndef NORADIUS |
| case VAR_RAD_ALIVE: |
| if (arg->argc > arg->argn + 2) { |
| log_Printf(LogWARN, "Too many RADIUS alive interval values\n"); |
| res = 1; |
| } else if (arg->argc == arg->argn) { |
| log_Printf(LogWARN, "Too few RADIUS alive interval values\n"); |
| res = 1; |
| } else { |
| arg->bundle->radius.alive.interval = atoi(argp); |
| if (arg->bundle->radius.alive.interval && !arg->bundle->radius.cfg.file) { |
| log_Printf(LogWARN, "rad_alive requires radius to be configured\n"); |
| res = 1; |
| } else if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) { |
| if (arg->bundle->radius.alive.interval) |
| radius_StartTimer(arg->bundle); |
| else |
| radius_StopTimer(&arg->bundle->radius); |
| } |
| } |
| break; |
| #endif |
| |
| case VAR_LQRPERIOD: |
| long_val = atol(argp); |
| if (long_val < MIN_LQRPERIOD) { |
| log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n", |
| long_val, MIN_LQRPERIOD); |
| res = 1; |
| } else |
| l->lcp.cfg.lqrperiod = long_val; |
| break; |
| |
| case VAR_LCPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &cx->physical->link.lcp.cfg.fsm.timeout, |
| &cx->physical->link.lcp.cfg.fsm.maxreq, |
| &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES); |
| break; |
| |
| case VAR_CHAPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &cx->chap.auth.cfg.fsm.timeout, |
| &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES); |
| break; |
| |
| case VAR_PAPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq, |
| NULL, DEF_FSMAUTHTRIES); |
| break; |
| |
| case VAR_CCPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq, |
| &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES); |
| break; |
| |
| case VAR_IPCPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &arg->bundle->ncp.ipcp.cfg.fsm.timeout, |
| &arg->bundle->ncp.ipcp.cfg.fsm.maxreq, |
| &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES); |
| break; |
| |
| #ifndef NOINET6 |
| case VAR_IPV6CPRETRY: |
| res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn, |
| &arg->bundle->ncp.ipv6cp.cfg.fsm.timeout, |
| &arg->bundle->ncp.ipv6cp.cfg.fsm.maxreq, |
| &arg->bundle->ncp.ipv6cp.cfg.fsm.maxtrm, DEF_FSMTRIES); |
| break; |
| #endif |
| |
| case VAR_NBNS: |
| case VAR_DNS: |
| if (param == VAR_DNS) { |
| ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns; |
| ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE; |
| } else { |
| ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns; |
| ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY; |
| } |
| |
| if (arg->argc > arg->argn) { |
| ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]); |
| if (!ncpaddr_getip4(ncpaddr, ipaddr)) |
| return -1; |
| if (arg->argc > arg->argn+1) { |
| ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]); |
| if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1)) |
| return -1; |
| } |
| |
| if (ipaddr[0].s_addr == INADDR_ANY) { |
| ipaddr[0] = ipaddr[1]; |
| ipaddr[1].s_addr = INADDR_ANY; |
| } |
| if (ipaddr[0].s_addr == INADDR_NONE) { |
| ipaddr[0] = ipaddr[1]; |
| ipaddr[1].s_addr = INADDR_NONE; |
| } |
| } |
| break; |
| |
| case VAR_CALLBACK: |
| cx->cfg.callback.opmask = 0; |
| for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) { |
| if (!strcasecmp(arg->argv[dummyint], "auth")) |
| cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH); |
| else if (!strcasecmp(arg->argv[dummyint], "cbcp")) |
| cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP); |
| else if (!strcasecmp(arg->argv[dummyint], "e.164")) { |
| if (dummyint == arg->argc - 1) |
| log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n"); |
| else { |
| cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164); |
| strncpy(cx->cfg.callback.msg, arg->argv[++dummyint], |
| sizeof cx->cfg.callback.msg - 1); |
| cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0'; |
| } |
| } else if (!strcasecmp(arg->argv[dummyint], "none")) |
| cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE); |
| else { |
| res = -1; |
| break; |
| } |
| } |
| if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE)) |
| cx->cfg.callback.opmask = 0; |
| break; |
| |
| case VAR_CBCP: |
| cx->cfg.cbcp.delay = 0; |
| *cx->cfg.cbcp.phone = '\0'; |
| cx->cfg.cbcp.fsmretry = DEF_FSMRETRY; |
| if (arg->argc > arg->argn) { |
| strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn], |
| sizeof cx->cfg.cbcp.phone - 1); |
| cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0'; |
| if (arg->argc > arg->argn + 1) { |
| cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]); |
| if (arg->argc > arg->argn + 2) { |
| long_val = atol(arg->argv[arg->argn + 2]); |
| if (long_val < MIN_FSMRETRY) |
| log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n", |
| long_val, MIN_FSMRETRY); |
| else |
| cx->cfg.cbcp.fsmretry = long_val; |
| } |
| } |
| } |
| break; |
| |
| case VAR_CHOKED: |
| arg->bundle->cfg.choked.timeout = atoi(argp); |
| if (arg->bundle->cfg.choked.timeout <= 0) |
| arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT; |
| break; |
| |
| case VAR_SENDPIPE: |
| long_val = atol(argp); |
| arg->bundle->ncp.cfg.sendpipe = long_val; |
| break; |
| |
| case VAR_RECVPIPE: |
| long_val = atol(argp); |
| arg->bundle->ncp.cfg.recvpipe = long_val; |
| break; |
| |
| #ifndef NORADIUS |
| case VAR_RADIUS: |
| if (!*argp) |
| *arg->bundle->radius.cfg.file = '\0'; |
| else if (access(argp, R_OK)) { |
| log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno)); |
| res = 1; |
| break; |
| } else { |
| strncpy(arg->bundle->radius.cfg.file, argp, |
| sizeof arg->bundle->radius.cfg.file - 1); |
| arg->bundle->radius.cfg.file |
| [sizeof arg->bundle->radius.cfg.file - 1] = '\0'; |
| } |
| break; |
| #endif |
| |
| case VAR_CD: |
| if (*argp) { |
| if (strcasecmp(argp, "off")) { |
| long_val = atol(argp); |
| if (long_val < 0) |
| long_val = 0; |
| cx->physical->cfg.cd.delay = long_val; |
| cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ? |
| CD_REQUIRED : CD_VARIABLE; |
| } else |
| cx->physical->cfg.cd.necessity = CD_NOTREQUIRED; |
| } else { |
| cx->physical->cfg.cd.delay = 0; |
| cx->physical->cfg.cd.necessity = CD_DEFAULT; |
| } |
| break; |
| |
| case VAR_PARITY: |
| if (arg->argc == arg->argn + 1) |
| res = physical_SetParity(arg->cx->physical, argp); |
| else { |
| log_Printf(LogWARN, "Parity value must be odd, even or none\n"); |
| res = 1; |
| } |
| break; |
| |
| case VAR_CRTSCTS: |
| if (strcasecmp(argp, "on") == 0) |
| physical_SetRtsCts(arg->cx->physical, 1); |
| else if (strcasecmp(argp, "off") == 0) |
| physical_SetRtsCts(arg->cx->physical, 0); |
| else { |
| log_Printf(LogWARN, "RTS/CTS value must be on or off\n"); |
| res = 1; |
| } |
| break; |
| |
| case VAR_URGENTPORTS: |
| if (arg->argn == arg->argc) { |
| ncp_SetUrgentTOS(&arg->bundle->ncp); |
| ncp_ClearUrgentTcpPorts(&arg->bundle->ncp); |
| ncp_ClearUrgentUdpPorts(&arg->bundle->ncp); |
| } else if (!strcasecmp(arg->argv[arg->argn], "udp")) { |
| ncp_SetUrgentTOS(&arg->bundle->ncp); |
| if (arg->argn == arg->argc - 1) |
| ncp_ClearUrgentUdpPorts(&arg->bundle->ncp); |
| else for (f = arg->argn + 1; f < arg->argc; f++) |
| if (*arg->argv[f] == '+') |
| ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1)); |
| else if (*arg->argv[f] == '-') |
| ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1)); |
| else { |
| if (f == arg->argn) |
| ncp_ClearUrgentUdpPorts(&arg->bundle->ncp); |
| ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f])); |
| } |
| } else if (arg->argn == arg->argc - 1 && |
| !strcasecmp(arg->argv[arg->argn], "none")) { |
| ncp_ClearUrgentTcpPorts(&arg->bundle->ncp); |
| ncp_ClearUrgentUdpPorts(&arg->bundle->ncp); |
| ncp_ClearUrgentTOS(&arg->bundle->ncp); |
| } else { |
| ncp_SetUrgentTOS(&arg->bundle->ncp); |
| first = arg->argn; |
| if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc) |
| ncp_ClearUrgentTcpPorts(&arg->bundle->ncp); |
| |
| for (f = first; f < arg->argc; f++) |
| if (*arg->argv[f] == '+') |
| ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1)); |
| else if (*arg->argv[f] == '-') |
| ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1)); |
| else { |
| if (f == first) |
| ncp_ClearUrgentTcpPorts(&arg->bundle->ncp); |
| ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f])); |
| } |
| } |
| break; |
| |
| case VAR_PPPOE: |
| if (strcasecmp(argp, "3Com") == 0) |
| physical_SetPPPoEnonstandard(arg->cx->physical, 1); |
| else if (strcasecmp(argp, "standard") == 0) |
| physical_SetPPPoEnonstandard(arg->cx->physical, 0); |
| else { |
| log_Printf(LogWARN, "PPPoE standard value must be \"standard\" or \"3Com\"\n"); |
| res = 1; |
| } |
| break; |
| |
| #ifndef NORADIUS |
| case VAR_PORT_ID: |
| if (strcasecmp(argp, "default") == 0) |
| arg->bundle->radius.port_id_type = RPI_DEFAULT; |
| else if (strcasecmp(argp, "pid") == 0) |
| arg->bundle->radius.port_id_type = RPI_PID; |
| else if (strcasecmp(argp, "ifnum") == 0) |
| arg->bundle->radius.port_id_type = RPI_IFNUM; |
| else if (strcasecmp(argp, "tunnum") == 0) |
| arg->bundle->radius.port_id_type = RPI_TUNNUM; |
| else { |
| log_Printf(LogWARN, |
| "RADIUS port id must be one of \"default\", \"pid\", \"ifnum\" or \"tunnum\"\n"); |
| res = 1; |
| } |
| |
| if (arg->bundle->radius.port_id_type && !arg->bundle->radius.cfg.file) { |
| log_Printf(LogWARN, "rad_port_id requires radius to be configured\n"); |
| res = 1; |
| } |
| |
| break; |
| #endif |
| } |
| |
| return res; |
| } |
| |
| static struct cmdtab const SetCommands[] = { |
| {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP}, |
| {"authkey", "key", SetVariable, LOCAL_AUTH, |
| "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY}, |
| {"authname", NULL, SetVariable, LOCAL_AUTH, |
| "authentication name", "set authname name", (const void *)VAR_AUTHNAME}, |
| {"autoload", NULL, SetVariable, LOCAL_AUTH, |
| "auto link [de]activation", "set autoload maxtime maxload mintime minload", |
| (const void *)VAR_AUTOLOAD}, |
| {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX, |
| "datalink bandwidth", "set bandwidth value", NULL}, |
| {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "callback control", "set callback [none|auth|cbcp|" |
| "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK}, |
| {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]", |
| (const void *)VAR_CBCP}, |
| {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, |
| "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY}, |
| {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement", |
| "set cd value[!]", (const void *)VAR_CD}, |
| {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "CHAP retries", "set chapretry value [attempts]", |
| (const void *)VAR_CHAPRETRY}, |
| {"choked", NULL, SetVariable, LOCAL_AUTH, |
| "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED}, |
| {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "Use hardware flow control", "set ctsrts [on|off]", |
| (const char *)VAR_CRTSCTS}, |
| {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, |
| "deflate window sizes", "set deflate out-winsize in-winsize", |
| (const void *) VAR_WINSIZE}, |
| #ifndef NODES |
| {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, |
| "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]", |
| (const void *) VAR_MPPE}, |
| #endif |
| {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "physical device name", "set device|line device-name[,device-name]", |
| (const void *) VAR_DEVICE}, |
| {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "dialing script", "set dial chat-script", (const void *) VAR_DIAL}, |
| {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server", |
| "set dns pri-addr [sec-addr]", (const void *)VAR_DNS}, |
| {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH, |
| "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]", NULL}, |
| {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX, |
| "escape characters", "set escape hex-digit ...", NULL}, |
| {"filter", NULL, filter_Set, LOCAL_AUTH, |
| "packet filters", "set filter alive|dial|in|out rule-no permit|deny " |
| "[src_addr[/width]] [dst_addr[/width]] [proto " |
| "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]", NULL}, |
| {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP}, |
| {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address", |
| "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]", NULL}, |
| {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue", |
| "set ifqueue packets", (const void *)VAR_IFQUEUE}, |
| {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries", |
| "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY}, |
| {"ipv6cpretry", "ipv6cpretries", SetVariable, LOCAL_AUTH, "IPV6CP retries", |
| "set ipv6cpretry value [attempts]", (const void *)VAR_IPV6CPRETRY}, |
| {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries", |
| "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY}, |
| {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level", |
| "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|" |
| "id0|ipcp|lcp|lqm|phase|physical|radius|sync|tcp/ip|timer|tun...", NULL}, |
| {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "login script", "set login chat-script", (const void *) VAR_LOGIN}, |
| {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "logout script", "set logout chat-script", (const void *) VAR_LOGOUT}, |
| {"lqrperiod", "echoperiod", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT, |
| "LQR period", "set lqr/echo period value", (const void *)VAR_LQRPERIOD}, |
| {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value", |
| "set mode interactive|auto|ddial|background", (const void *)VAR_MODE}, |
| {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value", |
| "set mrru value", (const void *)VAR_MRRU}, |
| {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU}, |
| {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU}, |
| {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server", |
| "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS}, |
| {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode", |
| "set openmode active|passive [secs]", (const void *)VAR_OPENMODE}, |
| {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries", |
| "set papretry value [attempts]", (const void *)VAR_PAPRETRY}, |
| {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity", |
| "set parity [odd|even|none]", (const void *)VAR_PARITY}, |
| {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)", |
| "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE}, |
| {"proctitle", "title", SetProcTitle, LOCAL_AUTH, |
| "Process title", "set proctitle [value]", NULL}, |
| #ifndef NORADIUS |
| {"radius", NULL, SetVariable, LOCAL_AUTH, |
| "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS}, |
| {"rad_alive", NULL, SetVariable, LOCAL_AUTH, |
| "Raduis alive interval", "set rad_alive value", |
| (const void *)VAR_RAD_ALIVE}, |
| {"rad_port_id", NULL, SetVariable, LOCAL_AUTH, |
| "NAS-Port-Id", "set rad_port_id [default|pid|ifnum|tunnum]", (const void *)VAR_PORT_ID}, |
| #endif |
| {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX, |
| "Reconnect timeout", "set reconnect value ntries", NULL}, |
| {"recvpipe", NULL, SetVariable, LOCAL_AUTH, |
| "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE}, |
| {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX, |
| "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]", NULL}, |
| {"sendpipe", NULL, SetVariable, LOCAL_AUTH, |
| "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE}, |
| {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port", |
| "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]", |
| NULL}, |
| {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX, |
| "physical speed", "set speed value|sync", NULL}, |
| {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX, |
| "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]", NULL}, |
| {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout", |
| "set timeout idletime", (const void *)VAR_IDLETIMEOUT}, |
| {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent ports", |
| "set urgent [tcp|udp] [+|-]port...", (const void *)VAR_URGENTPORTS}, |
| {"vj", NULL, ipcp_vjset, LOCAL_AUTH, |
| "vj values", "set vj slots|slotcomp [value]", NULL}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "set help|? [command]", SetCommands}, |
| {"pppoe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, |
| "Connect using standard/3Com mode", "set pppoe [standard|3Com]", |
| (const char *)VAR_PPPOE}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static int |
| SetCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc > arg->argn) |
| FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv, |
| arg->prompt, arg->cx); |
| else if (arg->prompt) |
| prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for" |
| " syntax help.\n"); |
| else |
| log_Printf(LogWARN, "set command must have arguments\n"); |
| |
| return 0; |
| } |
| |
| static int |
| AddCommand(struct cmdargs const *arg) |
| { |
| struct ncpaddr gw; |
| struct ncprange dest; |
| struct in_addr host; |
| #ifndef NOINET6 |
| struct in6_addr host6; |
| #endif |
| int dest_default, gw_arg, addrs; |
| |
| if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2) |
| return -1; |
| |
| addrs = 0; |
| dest_default = 0; |
| if (arg->argc == arg->argn + 2) { |
| if (!strcasecmp(arg->argv[arg->argn], "default")) |
| dest_default = 1; |
| else { |
| if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn])) |
| return -1; |
| if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6)) |
| addrs = ROUTE_DSTMYADDR; |
| else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7)) |
| addrs = ROUTE_DSTMYADDR6; |
| else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7)) |
| addrs = ROUTE_DSTHISADDR; |
| else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8)) |
| addrs = ROUTE_DSTHISADDR6; |
| else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4)) |
| addrs = ROUTE_DSTDNS0; |
| else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4)) |
| addrs = ROUTE_DSTDNS1; |
| } |
| gw_arg = 1; |
| } else { |
| if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) { |
| addrs = ROUTE_DSTMYADDR; |
| host = arg->bundle->ncp.ipcp.my_ip; |
| } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) { |
| addrs = ROUTE_DSTHISADDR; |
| host = arg->bundle->ncp.ipcp.peer_ip; |
| } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) { |
| addrs = ROUTE_DSTDNS0; |
| host = arg->bundle->ncp.ipcp.ns.dns[0]; |
| } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) { |
| addrs = ROUTE_DSTDNS1; |
| host = arg->bundle->ncp.ipcp.ns.dns[1]; |
| } else { |
| host = GetIpAddr(arg->argv[arg->argn]); |
| if (host.s_addr == INADDR_NONE) { |
| log_Printf(LogWARN, "%s: Invalid destination address\n", |
| arg->argv[arg->argn]); |
| return -1; |
| } |
| } |
| ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1])); |
| gw_arg = 2; |
| } |
| |
| if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) { |
| ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip); |
| addrs |= ROUTE_GWHISADDR; |
| #ifndef NOINET6 |
| } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) { |
| if (!ncpaddr_getip6(&arg->bundle->ncp.ipv6cp.hisaddr, &host6)) |
| memset(&host6, '\0', sizeof host6); |
| ncpaddr_setip6(&gw, &host6); |
| addrs |= ROUTE_GWHISADDR6; |
| #endif |
| } else { |
| if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) { |
| log_Printf(LogWARN, "%s: Invalid gateway address\n", |
| arg->argv[arg->argn + gw_arg]); |
| return -1; |
| } |
| } |
| |
| if (dest_default) |
| ncprange_setdefault(&dest, ncpaddr_family(&gw)); |
| |
| if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0, |
| ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0) |
| && addrs != ROUTE_STATIC) |
| route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw); |
| |
| return 0; |
| } |
| |
| static int |
| DeleteCommand(struct cmdargs const *arg) |
| { |
| struct ncprange dest; |
| int addrs; |
| |
| if (arg->argc == arg->argn+1) { |
| if(strcasecmp(arg->argv[arg->argn], "all") == 0) { |
| route_IfDelete(arg->bundle, 0); |
| route_DeleteAll(&arg->bundle->ncp.route); |
| } else { |
| addrs = 0; |
| if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) { |
| ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip); |
| addrs = ROUTE_DSTMYADDR; |
| #ifndef NOINET6 |
| } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) { |
| ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr); |
| addrs = ROUTE_DSTMYADDR6; |
| #endif |
| } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) { |
| ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip); |
| addrs = ROUTE_DSTHISADDR; |
| #ifndef NOINET6 |
| } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) { |
| ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr); |
| addrs = ROUTE_DSTHISADDR6; |
| #endif |
| } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) { |
| ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]); |
| addrs = ROUTE_DSTDNS0; |
| } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) { |
| ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]); |
| addrs = ROUTE_DSTDNS1; |
| } else { |
| ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]); |
| addrs = ROUTE_STATIC; |
| } |
| rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0); |
| route_Delete(&arg->bundle->ncp.route, addrs, &dest); |
| } |
| } else |
| return -1; |
| |
| return 0; |
| } |
| |
| #ifndef NONAT |
| static int |
| NatEnable(struct cmdargs const *arg) |
| { |
| if (arg->argc == arg->argn+1) { |
| if (strcasecmp(arg->argv[arg->argn], "yes") == 0) { |
| if (!arg->bundle->NatEnabled) { |
| if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) |
| PacketAliasSetAddress(arg->bundle->ncp.ipcp.my_ip); |
| arg->bundle->NatEnabled = 1; |
| } |
| return 0; |
| } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) { |
| arg->bundle->NatEnabled = 0; |
| opt_disable(arg->bundle, OPT_IFACEALIAS); |
| /* Don't iface_Clear() - there may be manually configured addresses */ |
| return 0; |
| } |
| } |
| |
| return -1; |
| } |
| |
| |
| static int |
| NatOption(struct cmdargs const *arg) |
| { |
| long param = (long)arg->cmd->args; |
| |
| if (arg->argc == arg->argn+1) { |
| if (strcasecmp(arg->argv[arg->argn], "yes") == 0) { |
| if (arg->bundle->NatEnabled) { |
| PacketAliasSetMode(param, param); |
| return 0; |
| } |
| log_Printf(LogWARN, "nat not enabled\n"); |
| } else if (strcmp(arg->argv[arg->argn], "no") == 0) { |
| if (arg->bundle->NatEnabled) { |
| PacketAliasSetMode(0, param); |
| return 0; |
| } |
| log_Printf(LogWARN, "nat not enabled\n"); |
| } |
| } |
| return -1; |
| } |
| #endif /* #ifndef NONAT */ |
| |
| static int |
| LinkCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc > arg->argn+1) { |
| char namelist[LINE_LEN]; |
| struct datalink *cx; |
| char *name; |
| int result = 0; |
| |
| if (!strcmp(arg->argv[arg->argn], "*")) { |
| struct datalink *dl; |
| |
| cx = arg->bundle->links; |
| while (cx) { |
| /* Watch it, the command could be a ``remove'' */ |
| dl = cx->next; |
| FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv, |
| arg->prompt, cx); |
| for (cx = arg->bundle->links; cx; cx = cx->next) |
| if (cx == dl) |
| break; /* Pointer's still valid ! */ |
| } |
| } else { |
| strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1); |
| namelist[sizeof namelist - 1] = '\0'; |
| for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) |
| if (!bundle2datalink(arg->bundle, name)) { |
| log_Printf(LogWARN, "link: %s: Invalid link name\n", name); |
| return 1; |
| } |
| |
| strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1); |
| namelist[sizeof namelist - 1] = '\0'; |
| for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) { |
| cx = bundle2datalink(arg->bundle, name); |
| if (cx) |
| FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv, |
| arg->prompt, cx); |
| else { |
| log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name); |
| result++; |
| } |
| } |
| } |
| return result; |
| } |
| |
| log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax); |
| return 2; |
| } |
| |
| struct link * |
| command_ChooseLink(struct cmdargs const *arg) |
| { |
| if (arg->cx) |
| return &arg->cx->physical->link; |
| else if (!arg->bundle->ncp.mp.cfg.mrru) { |
| struct datalink *dl = bundle2datalink(arg->bundle, NULL); |
| if (dl) |
| return &dl->physical->link; |
| } |
| return &arg->bundle->ncp.mp.link; |
| } |
| |
| static const char * |
| ident_cmd(const char *cmd, unsigned *keep, unsigned *add) |
| { |
| const char *result; |
| |
| switch (*cmd) { |
| case 'A': |
| case 'a': |
| result = "accept"; |
| *keep = NEG_MYMASK; |
| *add = NEG_ACCEPTED; |
| break; |
| case 'D': |
| case 'd': |
| switch (cmd[1]) { |
| case 'E': |
| case 'e': |
| result = "deny"; |
| *keep = NEG_MYMASK; |
| *add = 0; |
| break; |
| case 'I': |
| case 'i': |
| result = "disable"; |
| *keep = NEG_HISMASK; |
| *add = 0; |
| break; |
| default: |
| return NULL; |
| } |
| break; |
| case 'E': |
| case 'e': |
| result = "enable"; |
| *keep = NEG_HISMASK; |
| *add = NEG_ENABLED; |
| break; |
| default: |
| return NULL; |
| } |
| |
| return result; |
| } |
| |
| static int |
| OptSet(struct cmdargs const *arg) |
| { |
| int opt = (int)(long)arg->cmd->args; |
| unsigned keep; /* Keep this opt */ |
| unsigned add; /* Add this opt */ |
| |
| if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL) |
| return 1; |
| |
| #ifndef NOINET6 |
| if (add == NEG_ENABLED && opt == OPT_IPV6CP && !probe.ipv6_available) { |
| log_Printf(LogWARN, "IPv6 is not available on this machine\n"); |
| return 1; |
| } |
| #endif |
| if (!add && ((opt == OPT_NAS_IP_ADDRESS && |
| !Enabled(arg->bundle, OPT_NAS_IDENTIFIER)) || |
| (opt == OPT_NAS_IDENTIFIER && |
| !Enabled(arg->bundle, OPT_NAS_IP_ADDRESS)))) { |
| log_Printf(LogWARN, |
| "Cannot disable both NAS-IP-Address and NAS-Identifier\n"); |
| return 1; |
| } |
| |
| if (add) |
| opt_enable(arg->bundle, opt); |
| else |
| opt_disable(arg->bundle, opt); |
| |
| return 0; |
| } |
| |
| static int |
| IfaceAliasOptSet(struct cmdargs const *arg) |
| { |
| unsigned long long save = arg->bundle->cfg.optmask; |
| int result = OptSet(arg); |
| |
| if (result == 0) |
| if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) { |
| arg->bundle->cfg.optmask = save; |
| log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n"); |
| result = 2; |
| } |
| |
| return result; |
| } |
| |
| static int |
| NegotiateSet(struct cmdargs const *arg) |
| { |
| long param = (long)arg->cmd->args; |
| struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */ |
| struct datalink *cx = arg->cx; /* LOCAL_CX uses this */ |
| const char *cmd; |
| unsigned keep; /* Keep these bits */ |
| unsigned add; /* Add these bits */ |
| |
| if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL) |
| return 1; |
| |
| if ((arg->cmd->lauth & LOCAL_CX) && !cx) { |
| log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n", |
| cmd, arg->cmd->name); |
| return 2; |
| } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) { |
| log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n", |
| cmd, arg->cmd->name, cx->name); |
| cx = NULL; |
| } |
| |
| switch (param) { |
| case NEG_ACFCOMP: |
| cx->physical->link.lcp.cfg.acfcomp &= keep; |
| cx->physical->link.lcp.cfg.acfcomp |= add; |
| break; |
| case NEG_CHAP05: |
| cx->physical->link.lcp.cfg.chap05 &= keep; |
| cx->physical->link.lcp.cfg.chap05 |= add; |
| break; |
| #ifndef NODES |
| case NEG_CHAP80: |
| cx->physical->link.lcp.cfg.chap80nt &= keep; |
| cx->physical->link.lcp.cfg.chap80nt |= add; |
| break; |
| case NEG_CHAP80LM: |
| cx->physical->link.lcp.cfg.chap80lm &= keep; |
| cx->physical->link.lcp.cfg.chap80lm |= add; |
| break; |
| case NEG_CHAP81: |
| cx->physical->link.lcp.cfg.chap81 &= keep; |
| cx->physical->link.lcp.cfg.chap81 |= add; |
| break; |
| case NEG_MPPE: |
| l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep; |
| l->ccp.cfg.neg[CCP_NEG_MPPE] |= add; |
| break; |
| #endif |
| case NEG_DEFLATE: |
| l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep; |
| l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add; |
| break; |
| case NEG_DNS: |
| arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep; |
| arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add; |
| break; |
| case NEG_ECHO: /* probably misplaced in this function ! */ |
| if (cx->physical->link.lcp.cfg.echo && !add) { |
| cx->physical->link.lcp.cfg.echo = 0; |
| cx->physical->hdlc.lqm.method &= ~LQM_ECHO; |
| if (cx->physical->hdlc.lqm.method & LQM_ECHO && |
| !cx->physical->link.lcp.want_lqrperiod && |
| cx->physical->hdlc.lqm.timer.load) { |
| cx->physical->hdlc.lqm.timer.load = 0; |
| lqr_StopTimer(cx->physical); |
| } |
| } else if (!cx->physical->link.lcp.cfg.echo && add) { |
| cx->physical->link.lcp.cfg.echo = 1; |
| cx->physical->hdlc.lqm.method |= LQM_ECHO; |
| cx->physical->hdlc.lqm.timer.load = |
| cx->physical->link.lcp.cfg.lqrperiod * SECTICKS; |
| if (cx->physical->link.lcp.fsm.state == ST_OPENED) |
| (*cx->physical->hdlc.lqm.timer.func)(&cx->physical->link.lcp); |
| } |
| break; |
| case NEG_ENDDISC: |
| arg->bundle->ncp.mp.cfg.negenddisc &= keep; |
| arg->bundle->ncp.mp.cfg.negenddisc |= add; |
| break; |
| case NEG_LQR: |
| cx->physical->link.lcp.cfg.lqr &= keep; |
| cx->physical->link.lcp.cfg.lqr |= add; |
| break; |
| case NEG_PAP: |
| cx->physical->link.lcp.cfg.pap &= keep; |
| cx->physical->link.lcp.cfg.pap |= add; |
| break; |
| case NEG_PPPDDEFLATE: |
| l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep; |
| l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add; |
| break; |
| case NEG_PRED1: |
| l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep; |
| l->ccp.cfg.neg[CCP_NEG_PRED1] |= add; |
| break; |
| case NEG_PROTOCOMP: |
| cx->physical->link.lcp.cfg.protocomp &= keep; |
| cx->physical->link.lcp.cfg.protocomp |= add; |
| break; |
| case NEG_SHORTSEQ: |
| switch (bundle_Phase(arg->bundle)) { |
| case PHASE_DEAD: |
| break; |
| case PHASE_ESTABLISH: |
| /* Make sure none of our links are DATALINK_LCP or greater */ |
| if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) { |
| log_Printf(LogWARN, "shortseq: Only changable before" |
| " LCP negotiations\n"); |
| return 1; |
| } |
| break; |
| default: |
| log_Printf(LogWARN, "shortseq: Only changable at phase" |
| " DEAD/ESTABLISH\n"); |
| return 1; |
| } |
| arg->bundle->ncp.mp.cfg.shortseq &= keep; |
| arg->bundle->ncp.mp.cfg.shortseq |= add; |
| break; |
| case NEG_VJCOMP: |
| arg->bundle->ncp.ipcp.cfg.vj.neg &= keep; |
| arg->bundle->ncp.ipcp.cfg.vj.neg |= add; |
| break; |
| } |
| |
| return 0; |
| } |
| |
| static struct cmdtab const NegotiateCommands[] = { |
| {"echo", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, "Send echo requests", |
| "disable|enable", (const void *)NEG_ECHO}, |
| {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH, |
| "filter on PPPoUDP payloads", "disable|enable", |
| (const void *)OPT_FILTERDECAP}, |
| {"force-scripts", NULL, OptSet, LOCAL_AUTH, |
| "Force execution of the configured chat scripts", "disable|enable", |
| (const void *)OPT_FORCE_SCRIPTS}, |
| {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids", |
| "disable|enable", (const void *)OPT_IDCHECK}, |
| {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH, |
| "retain interface addresses", "disable|enable", |
| (const void *)OPT_IFACEALIAS}, |
| #ifndef NOINET6 |
| {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol", |
| "disable|enable", (const void *)OPT_IPCP}, |
| {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol", |
| "disable|enable", (const void *)OPT_IPV6CP}, |
| #endif |
| {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader", |
| "disable|enable", (const void *)OPT_KEEPSESSION}, |
| {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface", |
| "disable|enable", (const void *)OPT_LOOPBACK}, |
| {"nas-ip-address", NULL, OptSet, LOCAL_AUTH, "Send NAS-IP-Address to RADIUS", |
| "disable|enable", (const void *)OPT_NAS_IP_ADDRESS}, |
| {"nas-identifier", NULL, OptSet, LOCAL_AUTH, "Send NAS-Identifier to RADIUS", |
| "disable|enable", (const void *)OPT_NAS_IDENTIFIER}, |
| {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file", |
| "disable|enable", (const void *)OPT_PASSWDAUTH}, |
| {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry", |
| "disable|enable", (const void *)OPT_PROXY}, |
| {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts", |
| "disable|enable", (const void *)OPT_PROXYALL}, |
| {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes", |
| "disable|enable", (const void *)OPT_SROUTES}, |
| {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options", |
| "disable|enable", (const void *)OPT_TCPMSSFIXUP}, |
| {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput", |
| "disable|enable", (const void *)OPT_THROUGHPUT}, |
| {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp", |
| "disable|enable", (const void *)OPT_UTMP}, |
| |
| #ifndef NOINET6 |
| #define NEG_OPT_MAX 17 /* accept/deny allowed below and not above */ |
| #else |
| #define NEG_OPT_MAX 15 |
| #endif |
| |
| {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Address & Control field compression", "accept|deny|disable|enable", |
| (const void *)NEG_ACFCOMP}, |
| {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable", |
| (const void *)NEG_CHAP05}, |
| #ifndef NODES |
| {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Microsoft (NT) CHAP", "accept|deny|disable|enable", |
| (const void *)NEG_CHAP80}, |
| {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Microsoft (NT) CHAP", "accept|deny|disable|enable", |
| (const void *)NEG_CHAP80LM}, |
| {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Microsoft CHAP v2", "accept|deny|disable|enable", |
| (const void *)NEG_CHAP81}, |
| {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT, |
| "MPPE encryption", "accept|deny|disable|enable", |
| (const void *)NEG_MPPE}, |
| #endif |
| {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Deflate compression", "accept|deny|disable|enable", |
| (const void *)NEG_DEFLATE}, |
| {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Deflate (type 24) compression", "accept|deny|disable|enable", |
| (const void *)NEG_PPPDDEFLATE}, |
| {"dns", NULL, NegotiateSet, LOCAL_AUTH, |
| "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS}, |
| {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation", |
| "accept|deny|disable|enable", (const void *)NEG_ENDDISC}, |
| {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Link Quality Reports", "accept|deny|disable|enable", |
| (const void *)NEG_LQR}, |
| {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Password Authentication protocol", "accept|deny|disable|enable", |
| (const void *)NEG_PAP}, |
| {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT, |
| "Predictor 1 compression", "accept|deny|disable|enable", |
| (const void *)NEG_PRED1}, |
| {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, |
| "Protocol field compression", "accept|deny|disable|enable", |
| (const void *)NEG_PROTOCOMP}, |
| {"shortseq", NULL, NegotiateSet, LOCAL_AUTH, |
| "MP Short Sequence Numbers", "accept|deny|disable|enable", |
| (const void *)NEG_SHORTSEQ}, |
| {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH, |
| "Van Jacobson header compression", "accept|deny|disable|enable", |
| (const void *)NEG_VJCOMP}, |
| {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH, |
| "Display this message", "accept|deny|disable|enable help|? [value]", |
| NegotiateCommands}, |
| {NULL, NULL, NULL, 0, NULL, NULL, NULL}, |
| }; |
| |
| static int |
| NegotiateCommand(struct cmdargs const *arg) |
| { |
| if (arg->argc > arg->argn) { |
| char const *argv[3]; |
| unsigned keep, add; |
| int n; |
| |
| if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL) |
| return -1; |
| argv[2] = NULL; |
| |
| for (n = arg->argn; n < arg->argc; n++) { |
| argv[1] = arg->argv[n]; |
| FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ? |
| 0 : NEG_OPT_MAX), 2, 1, argv, arg->prompt, arg->cx); |
| } |
| } else if (arg->prompt) |
| prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n", |
| arg->argv[arg->argn-1]); |
| else |
| log_Printf(LogWARN, "%s command must have arguments\n", |
| arg->argv[arg->argn] ); |
| |
| return 0; |
| } |
| |
| const char * |
| command_ShowNegval(unsigned val) |
| { |
| switch (val&3) { |
| case 1: return "disabled & accepted"; |
| case 2: return "enabled & denied"; |
| case 3: return "enabled & accepted"; |
| } |
| return "disabled & denied"; |
| } |
| |
| static int |
| ClearCommand(struct cmdargs const *arg) |
| { |
| struct pppThroughput *t; |
| struct datalink *cx; |
| int i, clear_type; |
| |
| if (arg->argc < arg->argn + 1) |
| return -1; |
| |
| if (strcasecmp(arg->argv[arg->argn], "physical") == 0) { |
| cx = arg->cx; |
| if (!cx) |
| cx = bundle2datalink(arg->bundle, NULL); |
| if (!cx) { |
| log_Printf(LogWARN, "A link must be specified for ``clear physical''\n"); |
| return 1; |
| } |
| t = &cx->physical->link.stats.total; |
| } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0) |
| t = &arg->bundle->ncp.ipcp.throughput; |
| #ifndef NOINET6 |
| else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0) |
| t = &arg->bundle->ncp.ipv6cp.throughput; |
| #endif |
| else |
| return -1; |
| |
| if (arg->argc > arg->argn + 1) { |
| clear_type = 0; |
| for (i = arg->argn + 1; i < arg->argc; i++) |
| if (strcasecmp(arg->argv[i], "overall") == 0) |
| clear_type |= THROUGHPUT_OVERALL; |
| else if (strcasecmp(arg->argv[i], "current") == 0) |
| clear_type |= THROUGHPUT_CURRENT; |
| else if (strcasecmp(arg->argv[i], "peak") == 0) |
| clear_type |= THROUGHPUT_PEAK; |
| else |
| return -1; |
| } else |
| clear_type = THROUGHPUT_ALL; |
| |
| throughput_clear(t, clear_type, arg->prompt); |
| return 0; |
| } |
| |
| static int |
| RunListCommand(struct cmdargs const *arg) |
| { |
| const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???"; |
| |
| #ifndef NONAT |
| if (arg->cmd->args == NatCommands && |
| tolower(*arg->argv[arg->argn - 1]) == 'a') { |
| if (arg->prompt) |
| prompt_Printf(arg->prompt, "The alias command is deprecated\n"); |
| else |
| log_Printf(LogWARN, "The alias command is deprecated\n"); |
| } |
| #endif |
| |
| if (arg->argc > arg->argn) |
| FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv, |
| arg->prompt, arg->cx); |
| else if (arg->prompt) |
| prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help" |
| " <option>' for syntax help.\n", cmd, cmd); |
| else |
| log_Printf(LogWARN, "%s command must have arguments\n", cmd); |
| |
| return 0; |
| } |
| |
| static int |
| IfaceAddCommand(struct cmdargs const *arg) |
| { |
| struct ncpaddr peer, addr; |
| struct ncprange ifa; |
| struct in_addr mask; |
| int n, how; |
| |
| if (arg->argc == arg->argn + 1) { |
| if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn])) |
| return -1; |
| ncpaddr_init(&peer); |
| } else { |
| if (arg->argc == arg->argn + 2) { |
| if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn])) |
| return -1; |
| n = 1; |
| } else if (arg->argc == arg->argn + 3) { |
| if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn])) |
| return -1; |
| if (ncpaddr_family(&addr) != AF_INET) |
| return -1; |
| ncprange_sethost(&ifa, &addr); |
| if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1])) |
| return -1; |
| if (!ncpaddr_getip4(&addr, &mask)) |
| return -1; |
| if (!ncprange_setip4mask(&ifa, mask)) |
| return -1; |
| n = 2; |
| } else |
| return -1; |
| |
| if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n])) |
| return -1; |
| |
| if (ncprange_family(&ifa) != ncpaddr_family(&peer)) { |
| log_Printf(LogWARN, "IfaceAddCommand: src and dst address families" |
| " differ\n"); |
| return -1; |
| } |
| } |
| |
| how = IFACE_ADD_LAST; |
| if (arg->cmd->args) |
| how |= IFACE_FORCE_ADD; |
| |
| return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how); |
| } |
| |
| static int |
| IfaceDeleteCommand(struct cmdargs const *arg) |
| { |
| struct ncpaddr ifa; |
| struct in_addr ifa4; |
| int ok; |
| |
| if (arg->argc != arg->argn + 1) |
| return -1; |
| |
| if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn])) |
| return -1; |
| |
| if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED && |
| ncpaddr_getip4(&ifa, &ifa4) && |
| arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) { |
| log_Printf(LogWARN, "%s: Cannot remove active interface address\n", |
| ncpaddr_ntoa(&ifa)); |
| return 1; |
| } |
| |
| ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa); |
| if (!ok) { |
| if (arg->cmd->args) |
| ok = 1; |
| else if (arg->prompt) |
| prompt_Printf(arg->prompt, "%s: No such interface address\n", |
| ncpaddr_ntoa(&ifa)); |
| else |
| log_Printf(LogWARN, "%s: No such interface address\n", |
| ncpaddr_ntoa(&ifa)); |
| } |
| |
| return !ok; |
| } |
| |
| static int |
| IfaceClearCommand(struct cmdargs const *arg) |
| { |
| int family, how; |
| |
| family = 0; |
| if (arg->argc == arg->argn + 1) { |
| if (strcasecmp(arg->argv[arg->argn], "inet") == 0) |
| family = AF_INET; |
| #ifndef NOINET6 |
| else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0) |
| family = AF_INET6; |
| #endif |
| else |
| return -1; |
| } else if (arg->argc != arg->argn) |
| return -1; |
| |
| how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED || |
| arg->bundle->phys_type.all & PHYS_AUTO ? |
| IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL; |
| iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how); |
| |
| return 0; |
| } |
| |
| static int |
| SetProcTitle(struct cmdargs const *arg) |
| { |
| static char title[LINE_LEN]; |
| char *argv[MAXARGS]; |
| int argc = arg->argc - arg->argn; |
| |
| if (arg->argc <= arg->argn) { |
| SetTitle(NULL); |
| return 0; |
| } |
| |
| if ((unsigned)argc >= sizeof argv / sizeof argv[0]) { |
| argc = sizeof argv / sizeof argv[0] - 1; |
| log_Printf(LogWARN, "Truncating proc title to %d args\n", argc); |
| } |
| command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid()); |
| Concatinate(title, sizeof title, argc, (const char *const *)argv); |
| SetTitle(title); |
| command_Free(argc, argv); |
| |
| return 0; |
| } |