xref: /freebsd/usr.sbin/ppp/command.c (revision 0640d357f29fb1c0daaaffadd0416c5981413afd)
1 /*
2  *		PPP User command processing module
3  *
4  *	    Written by Toshiharu OHNO (tony-o@iij.ad.jp)
5  *
6  *   Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
7  *
8  * Redistribution and use in source and binary forms are permitted
9  * provided that the above copyright notice and this paragraph are
10  * duplicated in all such forms and that any documentation,
11  * advertising materials, and other materials related to such
12  * distribution and use acknowledge that the software was developed
13  * by the Internet Initiative Japan, Inc.  The name of the
14  * IIJ may not be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
18  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19  *
20  * $Id: command.c,v 1.174 1998/10/27 22:53:22 brian Exp $
21  *
22  */
23 #include <sys/types.h>
24 #include <netinet/in_systm.h>
25 #include <netinet/in.h>
26 #include <netinet/ip.h>
27 #include <arpa/inet.h>
28 #include <sys/socket.h>
29 #include <net/route.h>
30 #include <netdb.h>
31 #include <sys/un.h>
32 
33 #include <ctype.h>
34 #include <errno.h>
35 #include <fcntl.h>
36 #include <paths.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <sys/wait.h>
41 #include <termios.h>
42 #include <unistd.h>
43 
44 #ifndef NOALIAS
45 #ifdef __OpenBSD__
46 #include "alias.h"
47 #else
48 #include <alias.h>
49 #endif
50 #endif
51 #include "defs.h"
52 #include "command.h"
53 #include "mbuf.h"
54 #include "log.h"
55 #include "timer.h"
56 #include "fsm.h"
57 #include "lcp.h"
58 #include "iplist.h"
59 #include "throughput.h"
60 #include "slcompress.h"
61 #include "lqr.h"
62 #include "hdlc.h"
63 #include "ipcp.h"
64 #include "modem.h"
65 #ifndef NOALIAS
66 #include "alias_cmd.h"
67 #endif
68 #include "systems.h"
69 #include "filter.h"
70 #include "descriptor.h"
71 #include "main.h"
72 #include "route.h"
73 #include "ccp.h"
74 #include "auth.h"
75 #include "async.h"
76 #include "link.h"
77 #include "physical.h"
78 #include "mp.h"
79 #include "bundle.h"
80 #include "server.h"
81 #include "prompt.h"
82 #include "chat.h"
83 #include "chap.h"
84 #include "cbcp.h"
85 #include "datalink.h"
86 #include "iface.h"
87 
88 /* ``set'' values */
89 #define	VAR_AUTHKEY	0
90 #define	VAR_DIAL	1
91 #define	VAR_LOGIN	2
92 #define	VAR_AUTHNAME	3
93 #define	VAR_AUTOLOAD	4
94 #define	VAR_WINSIZE	5
95 #define	VAR_DEVICE	6
96 #define	VAR_ACCMAP	7
97 #define	VAR_MRRU	8
98 #define	VAR_MRU		9
99 #define	VAR_MTU		10
100 #define	VAR_OPENMODE	11
101 #define	VAR_PHONE	12
102 #define	VAR_HANGUP	13
103 #define	VAR_IDLETIMEOUT	14
104 #define	VAR_LQRPERIOD	15
105 #define	VAR_LCPRETRY	16
106 #define	VAR_CHAPRETRY	17
107 #define	VAR_PAPRETRY	18
108 #define	VAR_CCPRETRY	19
109 #define	VAR_IPCPRETRY	20
110 #define	VAR_DNS		21
111 #define	VAR_NBNS	22
112 #define	VAR_MODE	23
113 #define	VAR_CALLBACK	24
114 #define	VAR_CBCP	25
115 #define	VAR_CHOKED	26
116 #define	VAR_SENDPIPE	27
117 #define	VAR_RECVPIPE	28
118 
119 /* ``accept|deny|disable|enable'' masks */
120 #define NEG_HISMASK (1)
121 #define NEG_MYMASK (2)
122 
123 /* ``accept|deny|disable|enable'' values */
124 #define NEG_ACFCOMP	40
125 #define NEG_CHAP	41
126 #define NEG_DEFLATE	42
127 #define NEG_LQR		43
128 #define NEG_PAP		44
129 #define NEG_PPPDDEFLATE	45
130 #define NEG_PRED1	46
131 #define NEG_PROTOCOMP	47
132 #define NEG_SHORTSEQ	48
133 #define NEG_VJCOMP	49
134 #define NEG_DNS		50
135 
136 const char Version[] = "2.0";
137 const char VersionDate[] = "$Date: 1998/10/27 22:53:22 $";
138 
139 static int ShowCommand(struct cmdargs const *);
140 static int TerminalCommand(struct cmdargs const *);
141 static int QuitCommand(struct cmdargs const *);
142 static int OpenCommand(struct cmdargs const *);
143 static int CloseCommand(struct cmdargs const *);
144 static int DownCommand(struct cmdargs const *);
145 static int SetCommand(struct cmdargs const *);
146 static int LinkCommand(struct cmdargs const *);
147 static int AddCommand(struct cmdargs const *);
148 static int DeleteCommand(struct cmdargs const *);
149 static int NegotiateCommand(struct cmdargs const *);
150 static int ClearCommand(struct cmdargs const *);
151 static int RunListCommand(struct cmdargs const *);
152 static int IfaceAddCommand(struct cmdargs const *);
153 static int IfaceDeleteCommand(struct cmdargs const *);
154 static int IfaceClearCommand(struct cmdargs const *);
155 static int SetProcTitle(struct cmdargs const *);
156 #ifndef NOALIAS
157 static int AliasEnable(struct cmdargs const *);
158 static int AliasOption(struct cmdargs const *);
159 #endif
160 
161 static const char *
162 showcx(struct cmdtab const *cmd)
163 {
164   if (cmd->lauth & LOCAL_CX)
165     return "(c)";
166   else if (cmd->lauth & LOCAL_CX_OPT)
167     return "(o)";
168 
169   return "";
170 }
171 
172 static int
173 HelpCommand(struct cmdargs const *arg)
174 {
175   struct cmdtab const *cmd;
176   int n, cmax, dmax, cols, cxlen;
177   const char *cx;
178 
179   if (!arg->prompt) {
180     log_Printf(LogWARN, "help: Cannot help without a prompt\n");
181     return 0;
182   }
183 
184   if (arg->argc > arg->argn) {
185     for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
186       if ((cmd->lauth & arg->prompt->auth) &&
187           ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
188            (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
189 	prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
190 	return 0;
191       }
192     return -1;
193   }
194 
195   cmax = dmax = 0;
196   for (cmd = arg->cmdtab; cmd->func; cmd++)
197     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
198       if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
199         cmax = n;
200       if ((n = strlen(cmd->helpmes)) > dmax)
201         dmax = n;
202     }
203 
204   cols = 80 / (dmax + cmax + 3);
205   n = 0;
206   prompt_Printf(arg->prompt, "(o) = Optional context,"
207                 " (c) = Context required\n");
208   for (cmd = arg->cmdtab; cmd->func; cmd++)
209     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
210       cx = showcx(cmd);
211       cxlen = cmax - strlen(cmd->name);
212       if (n % cols != 0)
213         prompt_Printf(arg->prompt, " ");
214       prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
215               cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
216       if (++n % cols == 0)
217         prompt_Printf(arg->prompt, "\n");
218     }
219   if (n % cols != 0)
220     prompt_Printf(arg->prompt, "\n");
221 
222   return 0;
223 }
224 
225 static int
226 CloneCommand(struct cmdargs const *arg)
227 {
228   char namelist[LINE_LEN];
229   char *name;
230   int f;
231 
232   if (arg->argc == arg->argn)
233     return -1;
234 
235   namelist[sizeof namelist - 1] = '\0';
236   for (f = arg->argn; f < arg->argc; f++) {
237     strncpy(namelist, arg->argv[f], sizeof namelist - 1);
238     for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
239       bundle_DatalinkClone(arg->bundle, arg->cx, name);
240   }
241 
242   return 0;
243 }
244 
245 static int
246 RemoveCommand(struct cmdargs const *arg)
247 {
248   if (arg->argc != arg->argn)
249     return -1;
250 
251   if (arg->cx->state != DATALINK_CLOSED) {
252     log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
253     return 2;
254   }
255 
256   bundle_DatalinkRemove(arg->bundle, arg->cx);
257   return 0;
258 }
259 
260 static int
261 RenameCommand(struct cmdargs const *arg)
262 {
263   if (arg->argc != arg->argn + 1)
264     return -1;
265 
266   if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
267     return 0;
268 
269   log_Printf(LogWARN, "%s -> %s: target name already exists\n",
270              arg->cx->name, arg->argv[arg->argn]);
271   return 1;
272 }
273 
274 int
275 LoadCommand(struct cmdargs const *arg)
276 {
277   const char *err;
278   int n, mode;
279 
280   mode = arg->bundle->phys_type.all;
281 
282   if (arg->argn < arg->argc) {
283     for (n = arg->argn; n < arg->argc; n++)
284       if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
285         log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
286         return 1;
287       }
288 
289     for (n = arg->argn; n < arg->argc; n++) {
290       bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
291       system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
292     }
293     bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
294   } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
295     log_Printf(LogWARN, "default: %s\n", err);
296     return 1;
297   } else {
298     bundle_SetLabel(arg->bundle, "default");
299     system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
300     bundle_SetLabel(arg->bundle, "default");
301   }
302 
303   return 0;
304 }
305 
306 int
307 SaveCommand(struct cmdargs const *arg)
308 {
309   log_Printf(LogWARN, "save command is not implemented (yet).\n");
310   return 1;
311 }
312 
313 static int
314 DialCommand(struct cmdargs const *arg)
315 {
316   int res;
317 
318   if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
319       || (!arg->cx &&
320           (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
321     log_Printf(LogWARN, "Manual dial is only available for auto and"
322               " interactive links\n");
323     return 1;
324   }
325 
326   if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
327     return res;
328 
329   bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
330 
331   return 0;
332 }
333 
334 #define isinword(ch) (isalnum(ch) || (ch) == '_')
335 
336 static char *
337 strstrword(char *big, const char *little)
338 {
339   /* Get the first occurance of the word ``little'' in ``big'' */
340   char *pos;
341   int len;
342 
343   pos = big;
344   len = strlen(little);
345 
346   while ((pos = strstr(pos, little)) != NULL)
347     if ((pos == big || !isinword(pos[-1])) && !isinword(pos[len]))
348       break;
349     else
350       pos++;
351 
352   return pos;
353 }
354 
355 static char *
356 subst(char *tgt, const char *oldstr, const char *newstr)
357 {
358   /* tgt is a malloc()d area... realloc() as necessary */
359   char *word, *ntgt;
360   int ltgt, loldstr, lnewstr, pos;
361 
362   if ((word = strstrword(tgt, oldstr)) == NULL)
363     return tgt;
364 
365   ltgt = strlen(tgt) + 1;
366   loldstr = strlen(oldstr);
367   lnewstr = strlen(newstr);
368   do {
369     pos = word - tgt;
370     if (loldstr > lnewstr)
371       bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
372     if (loldstr != lnewstr) {
373       ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
374       if (ntgt == NULL)
375         break;			/* Oh wonderful ! */
376       word = ntgt + pos;
377       tgt = ntgt;
378     }
379     if (lnewstr > loldstr)
380       bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
381     bcopy(newstr, word, lnewstr);
382   } while ((word = strstrword(word, oldstr)));
383 
384   return tgt;
385 }
386 
387 static void
388 expand(char **nargv, int argc, char const *const *oargv, struct bundle *bundle)
389 {
390   int arg;
391   char pid[12];
392 
393   nargv[0] = strdup(oargv[0]);
394   snprintf(pid, sizeof pid, "%d", getpid());
395   for (arg = 1; arg < argc; arg++) {
396     nargv[arg] = strdup(oargv[arg]);
397     nargv[arg] = subst(nargv[arg], "HISADDR",
398                        inet_ntoa(bundle->ncp.ipcp.peer_ip));
399     nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
400     nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
401     nargv[arg] = subst(nargv[arg], "MYADDR", inet_ntoa(bundle->ncp.ipcp.my_ip));
402     nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
403     nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
404                        mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
405                                   bundle->ncp.mp.peer.enddisc.address,
406                                   bundle->ncp.mp.peer.enddisc.len));
407     nargv[arg] = subst(nargv[arg], "ENDDISC",
408                        mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
409                                   bundle->ncp.mp.cfg.enddisc.address,
410                                   bundle->ncp.mp.cfg.enddisc.len));
411     nargv[arg] = subst(nargv[arg], "PROCESSID", pid);
412     nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
413   }
414   nargv[arg] = NULL;
415 }
416 
417 static int
418 ShellCommand(struct cmdargs const *arg, int bg)
419 {
420   const char *shell;
421   pid_t shpid;
422 
423 #ifdef SHELL_ONLY_INTERACTIVELY
424   /* we're only allowed to shell when we run ppp interactively */
425   if (arg->prompt && arg->prompt->owner) {
426     log_Printf(LogWARN, "Can't start a shell from a network connection\n");
427     return 1;
428   }
429 #endif
430 
431   if (arg->argc == arg->argn) {
432     if (!arg->prompt) {
433       log_Printf(LogWARN, "Can't start an interactive shell from"
434                 " a config file\n");
435       return 1;
436     } else if (arg->prompt->owner) {
437       log_Printf(LogWARN, "Can't start an interactive shell from"
438                 " a socket connection\n");
439       return 1;
440     } else if (bg) {
441       log_Printf(LogWARN, "Can only start an interactive shell in"
442 		" the foreground mode\n");
443       return 1;
444     }
445   }
446 
447   if ((shpid = fork()) == 0) {
448     int i, fd;
449 
450     if ((shell = getenv("SHELL")) == 0)
451       shell = _PATH_BSHELL;
452 
453     timer_TermService();
454 
455     if (arg->prompt)
456       fd = arg->prompt->fd_out;
457     else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
458       log_Printf(LogALERT, "Failed to open %s: %s\n",
459                 _PATH_DEVNULL, strerror(errno));
460       exit(1);
461     }
462     for (i = 0; i < 3; i++)
463       dup2(fd, i);
464 
465     fcntl(3, F_SETFD, 1);	/* Set close-on-exec flag */
466 
467     setuid(geteuid());
468     if (arg->argc > arg->argn) {
469       /* substitute pseudo args */
470       char *argv[MAXARGS];
471       int argc = arg->argc - arg->argn;
472 
473       if (argc >= sizeof argv / sizeof argv[0]) {
474         argc = sizeof argv / sizeof argv[0] - 1;
475         log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
476       }
477       expand(argv, argc, arg->argv + arg->argn, arg->bundle);
478       if (bg) {
479 	pid_t p;
480 
481 	p = getpid();
482 	if (daemon(1, 1) == -1) {
483 	  log_Printf(LogERROR, "%d: daemon: %s\n", (int)p, strerror(errno));
484 	  exit(1);
485 	}
486       } else if (arg->prompt)
487         printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
488       execvp(argv[0], argv);
489     } else {
490       if (arg->prompt)
491         printf("ppp: Pausing until %s finishes\n", shell);
492       prompt_TtyOldMode(arg->prompt);
493       execl(shell, shell, NULL);
494     }
495 
496     log_Printf(LogWARN, "exec() of %s failed: %s\n",
497               arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
498               strerror(errno));
499     exit(255);
500   }
501 
502   if (shpid == (pid_t) - 1)
503     log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
504   else {
505     int status;
506     waitpid(shpid, &status, 0);
507   }
508 
509   if (arg->prompt && !arg->prompt->owner)
510     prompt_TtyCommandMode(arg->prompt);
511 
512   return 0;
513 }
514 
515 static int
516 BgShellCommand(struct cmdargs const *arg)
517 {
518   if (arg->argc == arg->argn)
519     return -1;
520   return ShellCommand(arg, 1);
521 }
522 
523 static int
524 FgShellCommand(struct cmdargs const *arg)
525 {
526   return ShellCommand(arg, 0);
527 }
528 
529 #ifndef NOALIAS
530 static struct cmdtab const AliasCommands[] =
531 {
532   {"addr", NULL, alias_RedirectAddr, LOCAL_AUTH,
533    "static address translation", "alias addr [addr_local addr_alias]"},
534   {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
535    "stop incoming connections", "alias deny_incoming [yes|no]",
536    (const void *) PKT_ALIAS_DENY_INCOMING},
537   {"enable", NULL, AliasEnable, LOCAL_AUTH,
538    "enable IP aliasing", "alias enable [yes|no]"},
539   {"log", NULL, AliasOption, LOCAL_AUTH,
540    "log aliasing link creation", "alias log [yes|no]",
541    (const void *) PKT_ALIAS_LOG},
542   {"port", NULL, alias_RedirectPort, LOCAL_AUTH,
543    "port redirection", "alias port [proto addr_local:port_local  port_alias]"},
544   {"same_ports", NULL, AliasOption, LOCAL_AUTH,
545    "try to leave port numbers unchanged", "alias same_ports [yes|no]",
546    (const void *) PKT_ALIAS_SAME_PORTS},
547   {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
548    "alias unregistered (private) IP address space only",
549    "alias unregistered_only [yes|no]",
550    (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
551   {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
552    "allocate host sockets", "alias use_sockets [yes|no]",
553    (const void *) PKT_ALIAS_USE_SOCKETS},
554   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
555    "Display this message", "alias help|? [command]", AliasCommands},
556   {NULL, NULL, NULL},
557 };
558 #endif
559 
560 static struct cmdtab const AllowCommands[] = {
561   {"modes", "mode", AllowModes, LOCAL_AUTH,
562   "Only allow certain ppp modes", "allow modes mode..."},
563   {"users", "user", AllowUsers, LOCAL_AUTH,
564   "Only allow ppp access to certain users", "allow users logname..."},
565   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
566   "Display this message", "allow help|? [command]", AllowCommands},
567   {NULL, NULL, NULL},
568 };
569 
570 static struct cmdtab const IfaceCommands[] =
571 {
572   {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
573    "Add iface address", "iface add addr[/bits| mask] peer", NULL},
574   {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
575    "Add or change an iface address", "iface add! addr[/bits| mask] peer",
576    (void *)1},
577   {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
578    "Clear iface address(es)", "iface clear"},
579   {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
580    "Delete iface address", "iface delete addr", NULL},
581   {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
582    "Delete iface address", "iface delete addr", (void *)1},
583   {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
584    "Delete iface address", "iface delete addr", (void *)1},
585   {"show", NULL, iface_Show, LOCAL_AUTH,
586    "Show iface address(es)", "iface show"},
587   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
588    "Display this message", "alias help|? [command]", IfaceCommands},
589   {NULL, NULL, NULL},
590 };
591 
592 static struct cmdtab const Commands[] = {
593   {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
594   "accept option request", "accept option .."},
595   {"add", NULL, AddCommand, LOCAL_AUTH,
596   "add route", "add dest mask gateway", NULL},
597   {NULL, "add!", AddCommand, LOCAL_AUTH,
598   "add or change route", "add! dest mask gateway", (void *)1},
599 #ifndef NOALIAS
600   {"alias", NULL, RunListCommand, LOCAL_AUTH,
601   "alias control", "alias option [yes|no]", AliasCommands},
602 #endif
603   {"allow", "auth", RunListCommand, LOCAL_AUTH,
604   "Allow ppp access", "allow users|modes ....", AllowCommands},
605   {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
606   "Run a background command", "[!]bg command"},
607   {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
608   "Clear throughput statistics", "clear ipcp|modem [current|overall|peak]..."},
609   {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
610   "Clone a link", "clone newname..."},
611   {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
612   "Close an FSM", "close [lcp|ccp]"},
613   {"delete", NULL, DeleteCommand, LOCAL_AUTH,
614   "delete route", "delete dest", NULL},
615   {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
616   "delete a route if it exists", "delete! dest", (void *)1},
617   {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
618   "Deny option request", "deny option .."},
619   {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
620   "Dial and login", "dial|call [system ...]", NULL},
621   {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
622   "Disable option", "disable option .."},
623   {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
624   "Generate a down event", "down"},
625   {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
626   "Enable option", "enable option .."},
627   {"iface", "interface", RunListCommand, LOCAL_AUTH,
628   "interface control", "iface option ...", IfaceCommands},
629   {"link", "datalink", LinkCommand, LOCAL_AUTH,
630   "Link specific commands", "link name command ..."},
631   {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
632   "Load settings", "load [system ...]"},
633   {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
634   "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
635   {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
636   "Password for manipulation", "passwd LocalPassword"},
637   {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
638   "Quit PPP program", "quit|bye [all]"},
639   {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
640   "Remove a link", "remove"},
641   {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
642   "Rename a link", "rename name"},
643   {"save", NULL, SaveCommand, LOCAL_AUTH,
644   "Save settings", "save"},
645   {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
646   "Set parameters", "set[up] var value"},
647   {"shell", "!", FgShellCommand, LOCAL_AUTH,
648   "Run a subshell", "shell|! [sh command]"},
649   {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
650   "Show status and stats", "show var"},
651   {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
652   "Enter terminal mode", "term"},
653   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
654   "Display this message", "help|? [command]", Commands},
655   {NULL, NULL, NULL},
656 };
657 
658 static int
659 ShowEscape(struct cmdargs const *arg)
660 {
661   if (arg->cx->physical->async.cfg.EscMap[32]) {
662     int code, bit;
663     const char *sep = "";
664 
665     for (code = 0; code < 32; code++)
666       if (arg->cx->physical->async.cfg.EscMap[code])
667 	for (bit = 0; bit < 8; bit++)
668 	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
669 	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
670             sep = ", ";
671           }
672     prompt_Printf(arg->prompt, "\n");
673   }
674   return 0;
675 }
676 
677 static int
678 ShowTimerList(struct cmdargs const *arg)
679 {
680   timer_Show(0, arg->prompt);
681   return 0;
682 }
683 
684 static int
685 ShowStopped(struct cmdargs const *arg)
686 {
687   prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
688   if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
689     prompt_Printf(arg->prompt, "Disabled");
690   else
691     prompt_Printf(arg->prompt, "%ld secs",
692                   arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
693 
694   prompt_Printf(arg->prompt, ", CCP: ");
695   if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
696     prompt_Printf(arg->prompt, "Disabled");
697   else
698     prompt_Printf(arg->prompt, "%ld secs",
699                   arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
700 
701   prompt_Printf(arg->prompt, "\n");
702 
703   return 0;
704 }
705 
706 static int
707 ShowVersion(struct cmdargs const *arg)
708 {
709   prompt_Printf(arg->prompt, "PPP Version %s - %s\n", Version, VersionDate);
710   return 0;
711 }
712 
713 static int
714 ShowProtocolStats(struct cmdargs const *arg)
715 {
716   struct link *l = command_ChooseLink(arg);
717 
718   prompt_Printf(arg->prompt, "%s:\n", l->name);
719   link_ReportProtocolStatus(l, arg->prompt);
720   return 0;
721 }
722 
723 static struct cmdtab const ShowCommands[] = {
724   {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
725   "bundle details", "show bundle"},
726   {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
727   "CCP status", "show cpp"},
728   {"compress", NULL, sl_Show, LOCAL_AUTH,
729   "VJ compression stats", "show compress"},
730   {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
731   "escape characters", "show escape"},
732   {"filter", NULL, filter_Show, LOCAL_AUTH,
733   "packet filters", "show filter [in|out|dial|alive]"},
734   {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
735   "HDLC errors", "show hdlc"},
736   {"iface", "interface", iface_Show, LOCAL_AUTH,
737   "Interface status", "show iface"},
738   {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
739   "IPCP status", "show ipcp"},
740   {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
741   "LCP status", "show lcp"},
742   {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
743   "(high-level) link info", "show link"},
744   {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
745   "available link names", "show links"},
746   {"log", NULL, log_ShowLevel, LOCAL_AUTH,
747   "log levels", "show log"},
748   {"mem", NULL, mbuf_Show, LOCAL_AUTH,
749   "mbuf allocations", "show mem"},
750   {"modem", NULL, modem_ShowStatus, LOCAL_AUTH | LOCAL_CX,
751   "(low-level) link info", "show modem"},
752   {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
753   "multilink setup", "show mp"},
754   {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
755   "protocol summary", "show proto"},
756   {"route", NULL, route_Show, LOCAL_AUTH,
757   "routing table", "show route"},
758   {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
759   "STOPPED timeout", "show stopped"},
760   {"timers", NULL, ShowTimerList, LOCAL_AUTH,
761   "alarm timers", "show timers"},
762   {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
763   "version string", "show version"},
764   {"who", NULL, log_ShowWho, LOCAL_AUTH,
765   "client list", "show who"},
766   {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
767   "Display this message", "show help|? [command]", ShowCommands},
768   {NULL, NULL, NULL},
769 };
770 
771 static struct cmdtab const *
772 FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
773 {
774   int nmatch;
775   int len;
776   struct cmdtab const *found;
777 
778   found = NULL;
779   len = strlen(str);
780   nmatch = 0;
781   while (cmds->func) {
782     if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
783       if (cmds->name[len] == '\0') {
784 	*pmatch = 1;
785 	return cmds;
786       }
787       nmatch++;
788       found = cmds;
789     } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
790       if (cmds->alias[len] == '\0') {
791 	*pmatch = 1;
792 	return cmds;
793       }
794       nmatch++;
795       found = cmds;
796     }
797     cmds++;
798   }
799   *pmatch = nmatch;
800   return found;
801 }
802 
803 static const char *
804 mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
805 {
806   int f, tlen, len;
807 
808   tlen = 0;
809   for (f = 0; f < argc && tlen < sz - 2; f++) {
810     if (f)
811       tgt[tlen++] = ' ';
812     len = strlen(argv[f]);
813     if (len > sz - tlen - 1)
814       len = sz - tlen - 1;
815     strncpy(tgt+tlen, argv[f], len);
816     tlen += len;
817   }
818   tgt[tlen] = '\0';
819   return tgt;
820 }
821 
822 static int
823 FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
824          char const *const *argv, struct prompt *prompt, struct datalink *cx)
825 {
826   struct cmdtab const *cmd;
827   int val = 1;
828   int nmatch;
829   struct cmdargs arg;
830   char prefix[100];
831 
832   cmd = FindCommand(cmds, argv[argn], &nmatch);
833   if (nmatch > 1)
834     log_Printf(LogWARN, "%s: Ambiguous command\n",
835               mkPrefix(argn+1, argv, prefix, sizeof prefix));
836   else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
837     if ((cmd->lauth & LOCAL_CX) && !cx)
838       /* We've got no context, but we require it */
839       cx = bundle2datalink(bundle, NULL);
840 
841     if ((cmd->lauth & LOCAL_CX) && !cx)
842       log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
843                 mkPrefix(argn+1, argv, prefix, sizeof prefix));
844     else {
845       if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
846         log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
847                   mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
848         cx = NULL;
849       }
850       arg.cmdtab = cmds;
851       arg.cmd = cmd;
852       arg.argc = argc;
853       arg.argn = argn+1;
854       arg.argv = argv;
855       arg.bundle = bundle;
856       arg.cx = cx;
857       arg.prompt = prompt;
858       val = (*cmd->func) (&arg);
859     }
860   } else
861     log_Printf(LogWARN, "%s: Invalid command\n",
862               mkPrefix(argn+1, argv, prefix, sizeof prefix));
863 
864   if (val == -1)
865     log_Printf(LogWARN, "Usage: %s\n", cmd->syntax);
866   else if (val)
867     log_Printf(LogWARN, "%s: Failed %d\n",
868               mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
869 
870   return val;
871 }
872 
873 int
874 command_Interpret(char *buff, int nb, char *argv[MAXARGS])
875 {
876   char *cp;
877 
878   if (nb > 0) {
879     cp = buff + strcspn(buff, "\r\n");
880     if (cp)
881       *cp = '\0';
882     return MakeArgs(buff, argv, MAXARGS);
883   }
884   return 0;
885 }
886 
887 static int
888 arghidden(int argc, char const *const *argv, int n)
889 {
890   /* Is arg n of the given command to be hidden from the log ? */
891 
892   /* set authkey xxxxx */
893   /* set key xxxxx */
894   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
895       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
896     return 1;
897 
898   /* passwd xxxxx */
899   if (n == 1 && !strncasecmp(argv[0], "p", 1))
900     return 1;
901 
902   /* set server port xxxxx .... */
903   if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
904       !strncasecmp(argv[1], "se", 2))
905     return 1;
906 
907   return 0;
908 }
909 
910 void
911 command_Run(struct bundle *bundle, int argc, char const *const *argv,
912            struct prompt *prompt, const char *label, struct datalink *cx)
913 {
914   if (argc > 0) {
915     if (log_IsKept(LogCOMMAND)) {
916       static char buf[LINE_LEN];
917       int f, n;
918 
919       *buf = '\0';
920       if (label) {
921         strncpy(buf, label, sizeof buf - 3);
922         buf[sizeof buf - 3] = '\0';
923         strcat(buf, ": ");
924       }
925       n = strlen(buf);
926       for (f = 0; f < argc; f++) {
927         if (n < sizeof buf - 1 && f)
928           buf[n++] = ' ';
929         if (arghidden(argc, argv, f))
930           strncpy(buf+n, "********", sizeof buf - n - 1);
931         else
932           strncpy(buf+n, argv[f], sizeof buf - n - 1);
933         n += strlen(buf+n);
934       }
935       log_Printf(LogCOMMAND, "%s\n", buf);
936     }
937     FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
938   }
939 }
940 
941 void
942 command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
943               const char *label)
944 {
945   int argc;
946   char *argv[MAXARGS];
947 
948   argc = command_Interpret(buff, nb, argv);
949   command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
950 }
951 
952 static int
953 ShowCommand(struct cmdargs const *arg)
954 {
955   if (!arg->prompt)
956     log_Printf(LogWARN, "show: Cannot show without a prompt\n");
957   else if (arg->argc > arg->argn)
958     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
959              arg->prompt, arg->cx);
960   else
961     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
962 
963   return 0;
964 }
965 
966 static int
967 TerminalCommand(struct cmdargs const *arg)
968 {
969   if (!arg->prompt) {
970     log_Printf(LogWARN, "term: Need a prompt\n");
971     return 1;
972   }
973 
974   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
975     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
976                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
977     return 1;
978   }
979 
980   datalink_Up(arg->cx, 0, 0);
981   prompt_TtyTermMode(arg->prompt, arg->cx);
982   return 0;
983 }
984 
985 static int
986 QuitCommand(struct cmdargs const *arg)
987 {
988   if (!arg->prompt || prompt_IsController(arg->prompt) ||
989       (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
990        (arg->prompt->auth & LOCAL_AUTH)))
991     Cleanup(EX_NORMAL);
992   if (arg->prompt)
993     prompt_Destroy(arg->prompt, 1);
994 
995   return 0;
996 }
997 
998 static int
999 OpenCommand(struct cmdargs const *arg)
1000 {
1001   if (arg->argc == arg->argn)
1002     bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
1003   else if (arg->argc == arg->argn + 1) {
1004     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1005       struct datalink *cx = arg->cx ?
1006         arg->cx : bundle2datalink(arg->bundle, NULL);
1007       if (cx) {
1008         if (cx->physical->link.lcp.fsm.state == ST_OPENED)
1009           fsm_Reopen(&cx->physical->link.lcp.fsm);
1010         else
1011           bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
1012       } else
1013         log_Printf(LogWARN, "open lcp: You must specify a link\n");
1014     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1015       struct fsm *fp;
1016 
1017       fp = &command_ChooseLink(arg)->ccp.fsm;
1018       if (fp->link->lcp.fsm.state != ST_OPENED)
1019         log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
1020       else if (fp->state == ST_OPENED)
1021         fsm_Reopen(fp);
1022       else {
1023         fp->open_mode = 0;	/* Not passive any more */
1024         if (fp->state == ST_STOPPED) {
1025           fsm_Down(fp);
1026           fsm_Up(fp);
1027         } else {
1028           fsm_Up(fp);
1029           fsm_Open(fp);
1030         }
1031       }
1032     } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
1033       if (arg->cx)
1034         log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
1035       if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
1036         fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
1037       else
1038         bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
1039     } else
1040       return -1;
1041   } else
1042     return -1;
1043 
1044   return 0;
1045 }
1046 
1047 static int
1048 CloseCommand(struct cmdargs const *arg)
1049 {
1050   if (arg->argc == arg->argn)
1051     bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
1052   else if (arg->argc == arg->argn + 1) {
1053     if (!strcasecmp(arg->argv[arg->argn], "lcp"))
1054       bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
1055     else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
1056              !strcasecmp(arg->argv[arg->argn], "ccp!")) {
1057       struct fsm *fp;
1058 
1059       fp = &command_ChooseLink(arg)->ccp.fsm;
1060       if (fp->state == ST_OPENED) {
1061         fsm_Close(fp);
1062         if (arg->argv[arg->argn][3] == '!')
1063           fp->open_mode = 0;		/* Stay ST_CLOSED */
1064         else
1065           fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
1066       }
1067     } else
1068       return -1;
1069   } else
1070     return -1;
1071 
1072   return 0;
1073 }
1074 
1075 static int
1076 DownCommand(struct cmdargs const *arg)
1077 {
1078   if (arg->argc == arg->argn) {
1079       if (arg->cx)
1080         datalink_Down(arg->cx, CLOSE_STAYDOWN);
1081       else
1082         bundle_Down(arg->bundle, CLOSE_STAYDOWN);
1083   } else if (arg->argc == arg->argn + 1) {
1084     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1085       if (arg->cx)
1086         datalink_Down(arg->cx, CLOSE_LCP);
1087       else
1088         bundle_Down(arg->bundle, CLOSE_LCP);
1089     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1090       struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
1091                                  &arg->bundle->ncp.mp.link.ccp.fsm;
1092       fsm2initial(fp);
1093     } else
1094       return -1;
1095   } else
1096     return -1;
1097 
1098   return 0;
1099 }
1100 
1101 static int
1102 SetModemSpeed(struct cmdargs const *arg)
1103 {
1104   long speed;
1105   char *end;
1106 
1107   if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
1108     if (arg->argc > arg->argn+1) {
1109       log_Printf(LogWARN, "SetModemSpeed: Too many arguments");
1110       return -1;
1111     }
1112     if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
1113       physical_SetSync(arg->cx->physical);
1114       return 0;
1115     }
1116     end = NULL;
1117     speed = strtol(arg->argv[arg->argn], &end, 10);
1118     if (*end) {
1119       log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
1120                 arg->argv[arg->argn]);
1121       return -1;
1122     }
1123     if (physical_SetSpeed(arg->cx->physical, speed))
1124       return 0;
1125     log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
1126   } else
1127     log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
1128 
1129   return -1;
1130 }
1131 
1132 static int
1133 SetStoppedTimeout(struct cmdargs const *arg)
1134 {
1135   struct link *l = &arg->cx->physical->link;
1136 
1137   l->lcp.fsm.StoppedTimer.load = 0;
1138   l->ccp.fsm.StoppedTimer.load = 0;
1139   if (arg->argc <= arg->argn+2) {
1140     if (arg->argc > arg->argn) {
1141       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
1142       if (arg->argc > arg->argn+1)
1143         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
1144     }
1145     return 0;
1146   }
1147   return -1;
1148 }
1149 
1150 #define ismask(x) \
1151   (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
1152 
1153 static int
1154 SetServer(struct cmdargs const *arg)
1155 {
1156   int res = -1;
1157 
1158   if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
1159     const char *port, *passwd, *mask;
1160 
1161     /* What's what ? */
1162     port = arg->argv[arg->argn];
1163     if (arg->argc == arg->argn + 2) {
1164       passwd = arg->argv[arg->argn+1];
1165       mask = NULL;
1166     } else if (arg->argc == arg->argn + 3) {
1167       passwd = arg->argv[arg->argn+1];
1168       mask = arg->argv[arg->argn+2];
1169       if (!ismask(mask))
1170         return -1;
1171     } else if (strcasecmp(port, "none") == 0) {
1172       if (server_Close(arg->bundle))
1173         log_Printf(LogPHASE, "Disabled server port.\n");
1174       return 0;
1175     } else
1176       return -1;
1177 
1178     strncpy(server.passwd, passwd, sizeof server.passwd - 1);
1179     server.passwd[sizeof server.passwd - 1] = '\0';
1180 
1181     if (*port == '/') {
1182       mode_t imask;
1183       char *ptr, name[LINE_LEN + 12];
1184 
1185       if (mask != NULL) {
1186 	unsigned m;
1187 
1188 	if (sscanf(mask, "%o", &m) == 1)
1189 	  imask = m;
1190         else
1191           return -1;
1192       } else
1193         imask = (mode_t)-1;
1194 
1195       ptr = strstr(port, "%d");
1196       if (ptr) {
1197         snprintf(name, sizeof name, "%.*s%d%s",
1198                  (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
1199         port = name;
1200       }
1201       res = server_LocalOpen(arg->bundle, port, imask);
1202     } else {
1203       int iport, add = 0;
1204 
1205       if (mask != NULL)
1206         return -1;
1207 
1208       if (*port == '+') {
1209         port++;
1210         add = 1;
1211       }
1212       if (strspn(port, "0123456789") != strlen(port)) {
1213         struct servent *s;
1214 
1215         if ((s = getservbyname(port, "tcp")) == NULL) {
1216 	  iport = 0;
1217 	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1218 	} else
1219 	  iport = ntohs(s->s_port);
1220       } else
1221         iport = atoi(port);
1222 
1223       if (iport) {
1224         if (add)
1225           iport += arg->bundle->unit;
1226         res = server_TcpOpen(arg->bundle, iport);
1227       } else
1228         res = -1;
1229     }
1230   }
1231 
1232   return res;
1233 }
1234 
1235 static int
1236 SetModemParity(struct cmdargs const *arg)
1237 {
1238   return arg->argc > arg->argn ? modem_SetParity(arg->cx->physical,
1239                                                  arg->argv[arg->argn]) : -1;
1240 }
1241 
1242 static int
1243 SetEscape(struct cmdargs const *arg)
1244 {
1245   int code;
1246   int argc = arg->argc - arg->argn;
1247   char const *const *argv = arg->argv + arg->argn;
1248 
1249   for (code = 0; code < 33; code++)
1250     arg->cx->physical->async.cfg.EscMap[code] = 0;
1251 
1252   while (argc-- > 0) {
1253     sscanf(*argv++, "%x", &code);
1254     code &= 0xff;
1255     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1256     arg->cx->physical->async.cfg.EscMap[32] = 1;
1257   }
1258   return 0;
1259 }
1260 
1261 static struct in_addr
1262 GetIpAddr(const char *cp)
1263 {
1264   struct hostent *hp;
1265   struct in_addr ipaddr;
1266 
1267   if (inet_aton(cp, &ipaddr) == 0) {
1268     hp = gethostbyname(cp);
1269     if (hp && hp->h_addrtype == AF_INET)
1270       memcpy(&ipaddr, hp->h_addr, hp->h_length);
1271     else
1272       ipaddr.s_addr = 0;
1273   }
1274   return (ipaddr);
1275 }
1276 
1277 static int
1278 SetInterfaceAddr(struct cmdargs const *arg)
1279 {
1280   struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
1281   const char *hisaddr;
1282 
1283   if (arg->argc > arg->argn + 4)
1284     return -1;
1285 
1286   hisaddr = NULL;
1287   ipcp->cfg.my_range.ipaddr.s_addr = INADDR_ANY;
1288   ipcp->cfg.peer_range.ipaddr.s_addr = INADDR_ANY;
1289   ipcp->cfg.HaveTriggerAddress = 0;
1290   ipcp->cfg.netmask.s_addr = INADDR_ANY;
1291   iplist_reset(&ipcp->cfg.peer_list);
1292 
1293   if (arg->argc > arg->argn) {
1294     if (!ParseAddr(ipcp, arg->argc - arg->argn, arg->argv + arg->argn,
1295                    &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask,
1296                    &ipcp->cfg.my_range.width))
1297       return 1;
1298     if (arg->argc > arg->argn+1) {
1299       hisaddr = arg->argv[arg->argn+1];
1300       if (arg->argc > arg->argn+2) {
1301         ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]);
1302 	if (arg->argc > arg->argn+3) {
1303 	  ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1304 	  ipcp->cfg.HaveTriggerAddress = 1;
1305 	}
1306       }
1307     }
1308   }
1309 
1310   /* 0.0.0.0 means any address (0 bits) */
1311   if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
1312     ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
1313     ipcp->cfg.my_range.width = 0;
1314   }
1315   ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
1316 
1317   if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1318                                   arg->bundle->phys_type.all & PHYS_AUTO))
1319     return 4;
1320 
1321   return 0;
1322 }
1323 
1324 static int
1325 SetVariable(struct cmdargs const *arg)
1326 {
1327   long long_val, param = (long)arg->cmd->args;
1328   int mode, dummyint;
1329   const char *argp;
1330   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1331   const char *err = NULL;
1332   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1333   struct in_addr dummyaddr, *addr;
1334 
1335   if (arg->argc > arg->argn)
1336     argp = arg->argv[arg->argn];
1337   else
1338     argp = "";
1339 
1340   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1341     log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1342               arg->cmd->name);
1343     return 1;
1344   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1345     log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1346               arg->cmd->name, cx->name);
1347     cx = NULL;
1348   }
1349 
1350   switch (param) {
1351   case VAR_AUTHKEY:
1352     switch (bundle_Phase(arg->bundle)) {
1353       case PHASE_DEAD:
1354       case PHASE_ESTABLISH:
1355         strncpy(arg->bundle->cfg.auth.key, argp,
1356                 sizeof arg->bundle->cfg.auth.key - 1);
1357         arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1358         break;
1359       default:
1360         err = "set authkey: Only available at phase DEAD/ESTABLISH\n";
1361         log_Printf(LogWARN, err);
1362         break;
1363     }
1364     break;
1365 
1366   case VAR_AUTHNAME:
1367     switch (bundle_Phase(arg->bundle)) {
1368       case PHASE_DEAD:
1369       case PHASE_ESTABLISH:
1370         strncpy(arg->bundle->cfg.auth.name, argp,
1371                 sizeof arg->bundle->cfg.auth.name - 1);
1372         arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
1373         break;
1374       default:
1375         err = "set authname: Only available at phase DEAD/ESTABLISH\n";
1376         log_Printf(LogWARN, err);
1377         break;
1378     }
1379     break;
1380 
1381   case VAR_AUTOLOAD:
1382     if (arg->argc == arg->argn + 2 || arg->argc == arg->argn + 4) {
1383       arg->bundle->autoload.running = 1;
1384       arg->bundle->cfg.autoload.max.timeout = atoi(arg->argv[arg->argn]);
1385       arg->bundle->cfg.autoload.max.packets = atoi(arg->argv[arg->argn + 1]);
1386       if (arg->argc == arg->argn + 4) {
1387         arg->bundle->cfg.autoload.min.timeout = atoi(arg->argv[arg->argn + 2]);
1388         arg->bundle->cfg.autoload.min.packets = atoi(arg->argv[arg->argn + 3]);
1389       } else {
1390         arg->bundle->cfg.autoload.min.timeout = 0;
1391         arg->bundle->cfg.autoload.min.packets = 0;
1392       }
1393     } else {
1394       err = "Set autoload requires two or four arguments\n";
1395       log_Printf(LogWARN, err);
1396     }
1397     break;
1398 
1399   case VAR_DIAL:
1400     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1401     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1402     break;
1403 
1404   case VAR_LOGIN:
1405     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1406     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1407     break;
1408 
1409   case VAR_WINSIZE:
1410     if (arg->argc > arg->argn) {
1411       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1412       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1413           l->ccp.cfg.deflate.out.winsize > 15) {
1414           log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1415                     l->ccp.cfg.deflate.out.winsize);
1416           l->ccp.cfg.deflate.out.winsize = 15;
1417       }
1418       if (arg->argc > arg->argn+1) {
1419         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1420         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1421             l->ccp.cfg.deflate.in.winsize > 15) {
1422             log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1423                       l->ccp.cfg.deflate.in.winsize);
1424             l->ccp.cfg.deflate.in.winsize = 15;
1425         }
1426       } else
1427         l->ccp.cfg.deflate.in.winsize = 0;
1428     } else {
1429       err = "No window size specified\n";
1430       log_Printf(LogWARN, err);
1431     }
1432     break;
1433 
1434   case VAR_DEVICE:
1435     physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1436                            arg->argv + arg->argn);
1437     break;
1438 
1439   case VAR_ACCMAP:
1440     if (arg->argc > arg->argn) {
1441       u_long ulong_val;
1442       sscanf(argp, "%lx", &ulong_val);
1443       cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
1444     } else {
1445       err = "No accmap specified\n";
1446       log_Printf(LogWARN, err);
1447     }
1448     break;
1449 
1450   case VAR_MODE:
1451     mode = Nam2mode(argp);
1452     if (mode == PHYS_NONE || mode == PHYS_ALL) {
1453       log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1454       return -1;
1455     }
1456     bundle_SetMode(arg->bundle, cx, mode);
1457     break;
1458 
1459   case VAR_MRRU:
1460     switch (bundle_Phase(arg->bundle)) {
1461       case PHASE_DEAD:
1462         break;
1463       case PHASE_ESTABLISH:
1464         /* Make sure none of our links are DATALINK_LCP or greater */
1465         if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
1466           log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
1467           return 1;
1468         }
1469         break;
1470       default:
1471         log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
1472         return 1;
1473     }
1474     long_val = atol(argp);
1475     if (long_val && long_val < MIN_MRU) {
1476       log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
1477       return 1;
1478     } else if (long_val > MAX_MRU) {
1479       log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
1480       return 1;
1481     } else
1482       arg->bundle->ncp.mp.cfg.mrru = long_val;
1483     break;
1484 
1485   case VAR_MRU:
1486     long_val = atol(argp);
1487     if (long_val == 0)
1488       l->lcp.cfg.mru = DEF_MRU;
1489     else if (long_val < MIN_MRU) {
1490       log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
1491       return 1;
1492     } else if (long_val > MAX_MRU) {
1493       log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
1494       return 1;
1495     } else
1496       l->lcp.cfg.mru = long_val;
1497     break;
1498 
1499   case VAR_MTU:
1500     long_val = atol(argp);
1501     if (long_val && long_val < MIN_MTU) {
1502       log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
1503       return 1;
1504     } else if (long_val > MAX_MTU) {
1505       log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
1506       return 1;
1507     } else
1508       arg->bundle->cfg.mtu = long_val;
1509     break;
1510 
1511   case VAR_OPENMODE:
1512     if (strcasecmp(argp, "active") == 0)
1513       cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
1514         atoi(arg->argv[arg->argn+1]) : 1;
1515     else if (strcasecmp(argp, "passive") == 0)
1516       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
1517     else {
1518       err = "%s: Invalid openmode\n";
1519       log_Printf(LogWARN, err, argp);
1520     }
1521     break;
1522 
1523   case VAR_PHONE:
1524     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
1525     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
1526     cx->phone.alt = cx->phone.next = NULL;
1527     break;
1528 
1529   case VAR_HANGUP:
1530     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
1531     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
1532     break;
1533 
1534   case VAR_IDLETIMEOUT:
1535     if (arg->argc > arg->argn+1)
1536       err = "Too many idle timeout values\n";
1537     else if (arg->argc == arg->argn+1)
1538       bundle_SetIdleTimer(arg->bundle, atoi(argp));
1539     if (err)
1540       log_Printf(LogWARN, err);
1541     break;
1542 
1543   case VAR_LQRPERIOD:
1544     long_val = atol(argp);
1545     if (long_val < MIN_LQRPERIOD) {
1546       log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
1547                  long_val, MIN_LQRPERIOD);
1548       return 1;
1549     } else
1550       l->lcp.cfg.lqrperiod = long_val;
1551     break;
1552 
1553   case VAR_LCPRETRY:
1554     long_val = atol(argp);
1555     if (long_val < MIN_FSMRETRY) {
1556       log_Printf(LogWARN, "%ld: Invalid LCP FSM retry period - min %d\n",
1557                  long_val, MIN_FSMRETRY);
1558       return 1;
1559     } else
1560       cx->physical->link.lcp.cfg.fsmretry = long_val;
1561     break;
1562 
1563   case VAR_CHAPRETRY:
1564     long_val = atol(argp);
1565     if (long_val < MIN_FSMRETRY) {
1566       log_Printf(LogWARN, "%ld: Invalid CHAP FSM retry period - min %d\n",
1567                  long_val, MIN_FSMRETRY);
1568       return 1;
1569     } else
1570       cx->chap.auth.cfg.fsmretry = long_val;
1571     break;
1572 
1573   case VAR_PAPRETRY:
1574     long_val = atol(argp);
1575     if (long_val < MIN_FSMRETRY) {
1576       log_Printf(LogWARN, "%ld: Invalid PAP FSM retry period - min %d\n",
1577                  long_val, MIN_FSMRETRY);
1578       return 1;
1579     } else
1580       cx->pap.cfg.fsmretry = long_val;
1581     break;
1582 
1583   case VAR_CCPRETRY:
1584     long_val = atol(argp);
1585     if (long_val < MIN_FSMRETRY) {
1586       log_Printf(LogWARN, "%ld: Invalid CCP FSM retry period - min %d\n",
1587                  long_val, MIN_FSMRETRY);
1588       return 1;
1589     } else
1590       l->ccp.cfg.fsmretry = long_val;
1591     break;
1592 
1593   case VAR_IPCPRETRY:
1594     long_val = atol(argp);
1595     if (long_val < MIN_FSMRETRY) {
1596       log_Printf(LogWARN, "%ld: Invalid IPCP FSM retry period - min %d\n",
1597                  long_val, MIN_FSMRETRY);
1598       return 1;
1599     } else
1600       arg->bundle->ncp.ipcp.cfg.fsmretry = long_val;
1601     break;
1602 
1603   case VAR_NBNS:
1604   case VAR_DNS:
1605     if (param == VAR_DNS)
1606       addr = arg->bundle->ncp.ipcp.cfg.ns.dns;
1607     else
1608       addr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
1609 
1610     addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
1611 
1612     if (arg->argc > arg->argn) {
1613       ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1614                 addr, &dummyaddr, &dummyint);
1615       if (arg->argc > arg->argn+1)
1616         ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn + 1,
1617                   addr + 1, &dummyaddr, &dummyint);
1618 
1619       if (addr[1].s_addr == INADDR_ANY)
1620         addr[1].s_addr = addr[0].s_addr;
1621       if (addr[0].s_addr == INADDR_ANY)
1622         addr[0].s_addr = addr[1].s_addr;
1623     }
1624     break;
1625 
1626   case VAR_CALLBACK:
1627     cx->cfg.callback.opmask = 0;
1628     for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
1629       if (!strcasecmp(arg->argv[dummyint], "auth"))
1630         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
1631       else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
1632         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
1633       else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
1634         if (dummyint == arg->argc - 1)
1635           log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
1636         else {
1637           cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
1638           strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
1639                   sizeof cx->cfg.callback.msg - 1);
1640           cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
1641         }
1642       } else if (!strcasecmp(arg->argv[dummyint], "none"))
1643         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
1644       else
1645         return -1;
1646     }
1647     if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
1648       cx->cfg.callback.opmask = 0;
1649     break;
1650 
1651   case VAR_CBCP:
1652     cx->cfg.cbcp.delay = 0;
1653     *cx->cfg.cbcp.phone = '\0';
1654     cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
1655     if (arg->argc > arg->argn) {
1656       strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
1657               sizeof cx->cfg.cbcp.phone - 1);
1658       cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
1659       if (arg->argc > arg->argn + 1) {
1660         cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
1661         if (arg->argc > arg->argn + 2) {
1662           long_val = atol(arg->argv[arg->argn + 2]);
1663           if (long_val < MIN_FSMRETRY)
1664             log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
1665                        long_val, MIN_FSMRETRY);
1666           else
1667             cx->cfg.cbcp.fsmretry = long_val;
1668         }
1669       }
1670     }
1671     break;
1672 
1673   case VAR_CHOKED:
1674     arg->bundle->cfg.choked.timeout = atoi(argp);
1675     if (arg->bundle->cfg.choked.timeout <= 0)
1676       arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
1677     break;
1678 
1679   case VAR_SENDPIPE:
1680     long_val = atol(argp);
1681     arg->bundle->ncp.ipcp.cfg.sendpipe = long_val;
1682     break;
1683 
1684   case VAR_RECVPIPE:
1685     long_val = atol(argp);
1686     arg->bundle->ncp.ipcp.cfg.recvpipe = long_val;
1687     break;
1688   }
1689 
1690   return err ? 1 : 0;
1691 }
1692 
1693 static int
1694 SetCtsRts(struct cmdargs const *arg)
1695 {
1696   if (arg->argc == arg->argn+1) {
1697     if (strcmp(arg->argv[arg->argn], "on") == 0)
1698       physical_SetRtsCts(arg->cx->physical, 1);
1699     else if (strcmp(arg->argv[arg->argn], "off") == 0)
1700       physical_SetRtsCts(arg->cx->physical, 0);
1701     else
1702       return -1;
1703     return 0;
1704   }
1705   return -1;
1706 }
1707 
1708 static struct cmdtab const SetCommands[] = {
1709   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1710   "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
1711   {"authkey", "key", SetVariable, LOCAL_AUTH,
1712   "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
1713   {"authname", NULL, SetVariable, LOCAL_AUTH,
1714   "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
1715   {"autoload", NULL, SetVariable, LOCAL_AUTH,
1716   "auto link [de]activation", "set autoload maxtime maxload mintime minload",
1717   (const void *)VAR_AUTOLOAD},
1718   {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1719   "callback control", "set callback [none|auth|cbcp|"
1720   "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
1721   {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1722   "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
1723   (const void *)VAR_CBCP},
1724   {"ccpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1725   "FSM retry period", "set ccpretry value", (const void *)VAR_CCPRETRY},
1726   {"chapretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1727   "CHAP retry period", "set chapretry value", (const void *)VAR_CHAPRETRY},
1728   {"choked", NULL, SetVariable, LOCAL_AUTH,
1729   "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
1730   {"ctsrts", "crtscts", SetCtsRts, LOCAL_AUTH | LOCAL_CX,
1731   "Use hardware flow control", "set ctsrts [on|off]"},
1732   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1733   "deflate window sizes", "set deflate out-winsize in-winsize",
1734   (const void *) VAR_WINSIZE},
1735   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
1736   "modem device name", "set device|line device-name[,device-name]",
1737   (const void *) VAR_DEVICE},
1738   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1739   "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
1740   {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
1741   "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
1742   {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
1743   "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]"},
1744   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
1745   "escape characters", "set escape hex-digit ..."},
1746   {"filter", NULL, filter_Set, LOCAL_AUTH,
1747   "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
1748   "[src_addr[/width]] [dst_addr[/width]] [tcp|udp|icmp [src [lt|eq|gt port]] "
1749   "[dst [lt|eq|gt port]] [estab] [syn] [finrst]]"},
1750   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1751   "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
1752   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
1753   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
1754   {"ipcpretry", NULL, SetVariable, LOCAL_AUTH,
1755   "FSM retry period", "set ipcpretry value", (const void *)VAR_IPCPRETRY},
1756   {"lcpretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1757   "FSM retry period", "set lcpretry value", (const void *)VAR_LCPRETRY},
1758   {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
1759   "set log [local] [+|-]async|cbcp|ccp|chat|command|connect|debug|hdlc|id0|"
1760   "ipcp|lcp|lqm|phase|tcp/ip|timer|tun..."},
1761   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1762   "login script", "set login chat-script", (const void *) VAR_LOGIN},
1763   {"lqrperiod", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1764   "LQR period", "set lqrperiod value", (const void *)VAR_LQRPERIOD},
1765   {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
1766   "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
1767   {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
1768   "set mrru value", (const void *)VAR_MRRU},
1769   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
1770   "MRU value", "set mru value", (const void *)VAR_MRU},
1771   {"mtu", NULL, SetVariable, LOCAL_AUTH,
1772   "interface MTU value", "set mtu value", (const void *)VAR_MTU},
1773   {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
1774   "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
1775   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
1776   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
1777   {"papretry", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
1778   "PAP retry period", "set papretry value", (const void *)VAR_PAPRETRY},
1779   {"parity", NULL, SetModemParity, LOCAL_AUTH | LOCAL_CX,
1780   "modem parity", "set parity [odd|even|none]"},
1781   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
1782   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
1783   {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
1784   "Process title", "set proctitle [value]"},
1785   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
1786   "Reconnect timeout", "set reconnect value ntries"},
1787   {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
1788   "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
1789   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
1790   "Redial timeout", "set redial value|random[.value|random] [attempts]"},
1791   {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
1792   "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
1793   {"server", "socket", SetServer, LOCAL_AUTH,
1794   "server port", "set server|socket TcpPort|LocalName|none password [mask]"},
1795   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
1796   "modem speed", "set speed value"},
1797   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
1798   "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]"},
1799   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
1800   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
1801   {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
1802   "vj values", "set vj slots|slotcomp [value]"},
1803   {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
1804   "datalink weighting", "set weight n"},
1805   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
1806   "Display this message", "set help|? [command]", SetCommands},
1807   {NULL, NULL, NULL},
1808 };
1809 
1810 static int
1811 SetCommand(struct cmdargs const *arg)
1812 {
1813   if (arg->argc > arg->argn)
1814     FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
1815              arg->prompt, arg->cx);
1816   else if (arg->prompt)
1817     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
1818 	    " syntax help.\n");
1819   else
1820     log_Printf(LogWARN, "set command must have arguments\n");
1821 
1822   return 0;
1823 }
1824 
1825 static int
1826 AddCommand(struct cmdargs const *arg)
1827 {
1828   struct in_addr dest, gateway, netmask;
1829   int gw, addrs;
1830 
1831   if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
1832     return -1;
1833 
1834   addrs = 0;
1835   if (arg->argc == arg->argn+2) {
1836     if (!strcasecmp(arg->argv[arg->argn], "default"))
1837       dest.s_addr = netmask.s_addr = INADDR_ANY;
1838     else {
1839       int width;
1840 
1841       if (!ParseAddr(&arg->bundle->ncp.ipcp, 1, arg->argv + arg->argn,
1842 	             &dest, &netmask, &width))
1843         return -1;
1844       if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
1845         addrs = ROUTE_DSTMYADDR;
1846       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
1847         addrs = ROUTE_DSTHISADDR;
1848     }
1849     gw = 1;
1850   } else {
1851     if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1852       addrs = ROUTE_DSTMYADDR;
1853       dest = arg->bundle->ncp.ipcp.my_ip;
1854     } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1855       addrs = ROUTE_DSTHISADDR;
1856       dest = arg->bundle->ncp.ipcp.peer_ip;
1857     } else
1858       dest = GetIpAddr(arg->argv[arg->argn]);
1859     netmask = GetIpAddr(arg->argv[arg->argn+1]);
1860     gw = 2;
1861   }
1862 
1863   if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) {
1864     gateway = arg->bundle->ncp.ipcp.peer_ip;
1865     addrs |= ROUTE_GWHISADDR;
1866   } else
1867     gateway = GetIpAddr(arg->argv[arg->argn+gw]);
1868 
1869   if (bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
1870                   arg->cmd->args ? 1 : 0, (addrs & ROUTE_GWHISADDR) ? 1 : 0))
1871     route_Add(&arg->bundle->ncp.ipcp.route, addrs, dest, netmask, gateway);
1872 
1873   return 0;
1874 }
1875 
1876 static int
1877 DeleteCommand(struct cmdargs const *arg)
1878 {
1879   struct in_addr dest, none;
1880   int addrs;
1881 
1882   if (arg->argc == arg->argn+1) {
1883     if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
1884       route_IfDelete(arg->bundle, 0);
1885       route_DeleteAll(&arg->bundle->ncp.ipcp.route);
1886     } else {
1887       addrs = 0;
1888       if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
1889         dest = arg->bundle->ncp.ipcp.my_ip;
1890         addrs = ROUTE_DSTMYADDR;
1891       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
1892         dest = arg->bundle->ncp.ipcp.peer_ip;
1893         addrs = ROUTE_DSTHISADDR;
1894       } else {
1895         if (strcasecmp(arg->argv[arg->argn], "default") == 0)
1896           dest.s_addr = INADDR_ANY;
1897         else
1898           dest = GetIpAddr(arg->argv[arg->argn]);
1899         addrs = ROUTE_STATIC;
1900       }
1901       none.s_addr = INADDR_ANY;
1902       bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
1903                       arg->cmd->args ? 1 : 0, 0);
1904       route_Delete(&arg->bundle->ncp.ipcp.route, addrs, dest);
1905     }
1906   } else
1907     return -1;
1908 
1909   return 0;
1910 }
1911 
1912 #ifndef NOALIAS
1913 static int
1914 AliasEnable(struct cmdargs const *arg)
1915 {
1916   if (arg->argc == arg->argn+1) {
1917     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1918       arg->bundle->AliasEnabled = 1;
1919       return 0;
1920     } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
1921       arg->bundle->AliasEnabled = 0;
1922       arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
1923       /* Don't iface_Clear() - there may be manually configured addresses */
1924       return 0;
1925     }
1926   }
1927 
1928   return -1;
1929 }
1930 
1931 
1932 static int
1933 AliasOption(struct cmdargs const *arg)
1934 {
1935   long param = (long)arg->cmd->args;
1936 
1937   if (arg->argc == arg->argn+1) {
1938     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
1939       if (arg->bundle->AliasEnabled) {
1940 	PacketAliasSetMode(param, param);
1941 	return 0;
1942       }
1943       log_Printf(LogWARN, "alias not enabled\n");
1944     } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
1945       if (arg->bundle->AliasEnabled) {
1946 	PacketAliasSetMode(0, param);
1947 	return 0;
1948       }
1949       log_Printf(LogWARN, "alias not enabled\n");
1950     }
1951   }
1952   return -1;
1953 }
1954 #endif /* #ifndef NOALIAS */
1955 
1956 static int
1957 LinkCommand(struct cmdargs const *arg)
1958 {
1959   if (arg->argc > arg->argn+1) {
1960     char namelist[LINE_LEN];
1961     struct datalink *cx;
1962     char *name;
1963     int result = 0;
1964 
1965     if (!strcmp(arg->argv[arg->argn], "*")) {
1966       struct datalink *dl;
1967 
1968       cx = arg->bundle->links;
1969       while (cx) {
1970         /* Watch it, the command could be a ``remove'' */
1971         dl = cx->next;
1972         FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1973                  arg->prompt, cx);
1974         for (cx = arg->bundle->links; cx; cx = cx->next)
1975           if (cx == dl)
1976             break;		/* Pointer's still valid ! */
1977       }
1978     } else {
1979       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1980       namelist[sizeof namelist - 1] = '\0';
1981       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
1982         if (!bundle2datalink(arg->bundle, name)) {
1983           log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
1984           return 1;
1985         }
1986 
1987       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
1988       namelist[sizeof namelist - 1] = '\0';
1989       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
1990         cx = bundle2datalink(arg->bundle, name);
1991         if (cx)
1992           FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
1993                    arg->prompt, cx);
1994         else {
1995           log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
1996           result++;
1997         }
1998       }
1999     }
2000     return result;
2001   }
2002 
2003   log_Printf(LogWARN, "Usage: %s\n", arg->cmd->syntax);
2004   return 2;
2005 }
2006 
2007 struct link *
2008 command_ChooseLink(struct cmdargs const *arg)
2009 {
2010   if (arg->cx)
2011     return &arg->cx->physical->link;
2012   else if (!arg->bundle->ncp.mp.cfg.mrru) {
2013     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
2014     if (dl)
2015       return &dl->physical->link;
2016   }
2017   return &arg->bundle->ncp.mp.link;
2018 }
2019 
2020 static const char *
2021 ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
2022 {
2023   const char *result;
2024 
2025   switch (*cmd) {
2026     case 'A':
2027     case 'a':
2028       result = "accept";
2029       *keep = NEG_MYMASK;
2030       *add = NEG_ACCEPTED;
2031       break;
2032     case 'D':
2033     case 'd':
2034       switch (cmd[1]) {
2035         case 'E':
2036         case 'e':
2037           result = "deny";
2038           *keep = NEG_MYMASK;
2039           *add = 0;
2040           break;
2041         case 'I':
2042         case 'i':
2043           result = "disable";
2044           *keep = NEG_HISMASK;
2045           *add = 0;
2046           break;
2047         default:
2048           return NULL;
2049       }
2050       break;
2051     case 'E':
2052     case 'e':
2053       result = "enable";
2054       *keep = NEG_HISMASK;
2055       *add = NEG_ENABLED;
2056       break;
2057     default:
2058       return NULL;
2059   }
2060 
2061   return result;
2062 }
2063 
2064 static int
2065 OptSet(struct cmdargs const *arg)
2066 {
2067   int bit = (int)(long)arg->cmd->args;
2068   const char *cmd;
2069   unsigned keep;			/* Keep these bits */
2070   unsigned add;				/* Add these bits */
2071 
2072   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
2073     return 1;
2074 
2075   if (add)
2076     arg->bundle->cfg.opt |= bit;
2077   else
2078     arg->bundle->cfg.opt &= ~bit;
2079   return 0;
2080 }
2081 
2082 static int
2083 IfaceAliasOptSet(struct cmdargs const *arg)
2084 {
2085   unsigned save = arg->bundle->cfg.opt;
2086   int result = OptSet(arg);
2087 
2088   if (result == 0)
2089     if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->AliasEnabled) {
2090       arg->bundle->cfg.opt = save;
2091       log_Printf(LogWARN, "Cannot enable iface-alias without IP aliasing\n");
2092       result = 2;
2093     }
2094 
2095   return result;
2096 }
2097 
2098 static int
2099 NegotiateSet(struct cmdargs const *arg)
2100 {
2101   long param = (long)arg->cmd->args;
2102   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
2103   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
2104   const char *cmd;
2105   unsigned keep;			/* Keep these bits */
2106   unsigned add;				/* Add these bits */
2107 
2108   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
2109     return 1;
2110 
2111   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
2112     log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
2113               cmd, arg->cmd->name);
2114     return 2;
2115   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
2116     log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
2117               cmd, arg->cmd->name, cx->name);
2118     cx = NULL;
2119   }
2120 
2121   switch (param) {
2122     case NEG_ACFCOMP:
2123       cx->physical->link.lcp.cfg.acfcomp &= keep;
2124       cx->physical->link.lcp.cfg.acfcomp |= add;
2125       break;
2126     case NEG_CHAP:
2127       cx->physical->link.lcp.cfg.chap &= keep;
2128       cx->physical->link.lcp.cfg.chap |= add;
2129       break;
2130     case NEG_DEFLATE:
2131       l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
2132       l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
2133       break;
2134     case NEG_DNS:
2135       arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
2136       arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
2137       break;
2138     case NEG_LQR:
2139       cx->physical->link.lcp.cfg.lqr &= keep;
2140       cx->physical->link.lcp.cfg.lqr |= add;
2141       break;
2142     case NEG_PAP:
2143       cx->physical->link.lcp.cfg.pap &= keep;
2144       cx->physical->link.lcp.cfg.pap |= add;
2145       break;
2146     case NEG_PPPDDEFLATE:
2147       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
2148       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
2149       break;
2150     case NEG_PRED1:
2151       l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
2152       l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
2153       break;
2154     case NEG_PROTOCOMP:
2155       cx->physical->link.lcp.cfg.protocomp &= keep;
2156       cx->physical->link.lcp.cfg.protocomp |= add;
2157       break;
2158     case NEG_SHORTSEQ:
2159       switch (bundle_Phase(arg->bundle)) {
2160         case PHASE_DEAD:
2161           break;
2162         case PHASE_ESTABLISH:
2163           /* Make sure none of our links are DATALINK_LCP or greater */
2164           if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
2165             log_Printf(LogWARN, "shortseq: Only changable before"
2166                        " LCP negotiations\n");
2167             return 1;
2168           }
2169           break;
2170         default:
2171           log_Printf(LogWARN, "shortseq: Only changable at phase"
2172                      " DEAD/ESTABLISH\n");
2173           return 1;
2174       }
2175       arg->bundle->ncp.mp.cfg.shortseq &= keep;
2176       arg->bundle->ncp.mp.cfg.shortseq |= add;
2177       break;
2178     case NEG_VJCOMP:
2179       arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
2180       arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
2181       break;
2182   }
2183 
2184   return 0;
2185 }
2186 
2187 static struct cmdtab const NegotiateCommands[] = {
2188   {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
2189   "disable|enable", (const void *)OPT_IDCHECK},
2190   {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
2191    "retain interface addresses", "disable|enable",
2192    (const void *)OPT_IFACEALIAS},
2193   {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
2194   "disable|enable", (const void *)OPT_LOOPBACK},
2195   {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
2196   "disable|enable", (const void *)OPT_PASSWDAUTH},
2197   {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
2198   "disable|enable", (const void *)OPT_PROXY},
2199   {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
2200   "disable|enable", (const void *)OPT_PROXYALL},
2201   {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
2202   "disable|enable", (const void *)OPT_SROUTES},
2203   {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
2204   "disable|enable", (const void *)OPT_THROUGHPUT},
2205   {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
2206   "disable|enable", (const void *)OPT_UTMP},
2207 
2208 #define OPT_MAX 9	/* accept/deny allowed below and not above */
2209 
2210   {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2211   "Address & Control field compression", "accept|deny|disable|enable",
2212   (const void *)NEG_ACFCOMP},
2213   {"chap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2214   "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
2215   (const void *)NEG_CHAP},
2216   {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2217   "Deflate compression", "accept|deny|disable|enable",
2218   (const void *)NEG_DEFLATE},
2219   {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2220   "Deflate (type 24) compression", "accept|deny|disable|enable",
2221   (const void *)NEG_PPPDDEFLATE},
2222   {"dns", NULL, NegotiateSet, LOCAL_AUTH,
2223   "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
2224   {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2225   "Link Quality Reports", "accept|deny|disable|enable",
2226   (const void *)NEG_LQR},
2227   {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2228   "Password Authentication protocol", "accept|deny|disable|enable",
2229   (const void *)NEG_PAP},
2230   {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
2231   "Predictor 1 compression", "accept|deny|disable|enable",
2232   (const void *)NEG_PRED1},
2233   {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
2234   "Protocol field compression", "accept|deny|disable|enable",
2235   (const void *)NEG_PROTOCOMP},
2236   {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
2237   "MP Short Sequence Numbers", "accept|deny|disable|enable",
2238   (const void *)NEG_SHORTSEQ},
2239   {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
2240   "Van Jacobson header compression", "accept|deny|disable|enable",
2241   (const void *)NEG_VJCOMP},
2242   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2243   "Display this message", "accept|deny|disable|enable help|? [value]",
2244   NegotiateCommands},
2245   {NULL, NULL, NULL},
2246 };
2247 
2248 static int
2249 NegotiateCommand(struct cmdargs const *arg)
2250 {
2251   if (arg->argc > arg->argn) {
2252     char const *argv[3];
2253     unsigned keep, add;
2254     int n;
2255 
2256     if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
2257       return -1;
2258     argv[2] = NULL;
2259 
2260     for (n = arg->argn; n < arg->argc; n++) {
2261       argv[1] = arg->argv[n];
2262       FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
2263                0 : OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
2264     }
2265   } else if (arg->prompt)
2266     prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
2267 	    arg->argv[arg->argn-1]);
2268   else
2269     log_Printf(LogWARN, "%s command must have arguments\n",
2270               arg->argv[arg->argn] );
2271 
2272   return 0;
2273 }
2274 
2275 const char *
2276 command_ShowNegval(unsigned val)
2277 {
2278   switch (val&3) {
2279     case 1: return "disabled & accepted";
2280     case 2: return "enabled & denied";
2281     case 3: return "enabled & accepted";
2282   }
2283   return "disabled & denied";
2284 }
2285 
2286 static int
2287 ClearCommand(struct cmdargs const *arg)
2288 {
2289   struct pppThroughput *t;
2290   struct datalink *cx;
2291   int i, clear_type;
2292 
2293   if (arg->argc < arg->argn + 1)
2294     return -1;
2295 
2296   if (strcasecmp(arg->argv[arg->argn], "modem") == 0) {
2297     cx = arg->cx;
2298     if (!cx)
2299       cx = bundle2datalink(arg->bundle, NULL);
2300     if (!cx) {
2301       log_Printf(LogWARN, "A link must be specified for ``clear modem''\n");
2302       return 1;
2303     }
2304     t = &cx->physical->link.throughput;
2305   } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
2306     t = &arg->bundle->ncp.ipcp.throughput;
2307   else
2308     return -1;
2309 
2310   if (arg->argc > arg->argn + 1) {
2311     clear_type = 0;
2312     for (i = arg->argn + 1; i < arg->argc; i++)
2313       if (strcasecmp(arg->argv[i], "overall") == 0)
2314         clear_type |= THROUGHPUT_OVERALL;
2315       else if (strcasecmp(arg->argv[i], "current") == 0)
2316         clear_type |= THROUGHPUT_CURRENT;
2317       else if (strcasecmp(arg->argv[i], "peak") == 0)
2318         clear_type |= THROUGHPUT_PEAK;
2319       else
2320         return -1;
2321   } else
2322     clear_type = THROUGHPUT_ALL;
2323 
2324   throughput_clear(t, clear_type, arg->prompt);
2325   return 0;
2326 }
2327 
2328 static int
2329 RunListCommand(struct cmdargs const *arg)
2330 {
2331   const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
2332 
2333   if (arg->argc > arg->argn)
2334     FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
2335              arg->prompt, arg->cx);
2336   else if (arg->prompt)
2337     prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
2338                   " <option>' for syntax help.\n", cmd, cmd);
2339   else
2340     log_Printf(LogWARN, "%s command must have arguments\n", cmd);
2341 
2342   return 0;
2343 }
2344 
2345 static int
2346 IfaceAddCommand(struct cmdargs const *arg)
2347 {
2348   int bits, n, how;
2349   struct in_addr ifa, mask, brd;
2350 
2351   if (arg->argc == arg->argn + 1) {
2352     if (!ParseAddr(NULL, 1, arg->argv + arg->argn, &ifa, NULL, NULL))
2353       return -1;
2354     mask.s_addr = brd.s_addr = INADDR_BROADCAST;
2355   } else {
2356     if (arg->argc == arg->argn + 2) {
2357       if (!ParseAddr(NULL, 1, arg->argv + arg->argn, &ifa, &mask, &bits))
2358         return -1;
2359       n = 1;
2360     } else if (arg->argc == arg->argn + 3) {
2361       if (!ParseAddr(NULL, 1, arg->argv + arg->argn, &ifa, NULL, NULL))
2362         return -1;
2363       if (!ParseAddr(NULL, 1, arg->argv + arg->argn + 1, &mask, NULL, NULL))
2364         return -1;
2365       n = 2;
2366     } else
2367       return -1;
2368 
2369     if (!ParseAddr(NULL, 1, arg->argv + arg->argn + n, &brd, NULL, NULL))
2370       return -1;
2371   }
2372 
2373   how = IFACE_ADD_LAST;
2374   if (arg->cmd->args)
2375     how |= IFACE_FORCE_ADD;
2376 
2377   return !iface_inAdd(arg->bundle->iface, ifa, mask, brd, how);
2378 }
2379 
2380 static int
2381 IfaceDeleteCommand(struct cmdargs const *arg)
2382 {
2383   struct in_addr ifa;
2384   int ok;
2385 
2386   if (arg->argc != arg->argn + 1)
2387     return -1;
2388 
2389   if (!ParseAddr(NULL, 1, arg->argv + arg->argn, &ifa, NULL, NULL))
2390     return -1;
2391 
2392   if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
2393       arg->bundle->ncp.ipcp.my_ip.s_addr == ifa.s_addr) {
2394     log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
2395                inet_ntoa(ifa));
2396     return 1;
2397   }
2398 
2399   ok = iface_inDelete(arg->bundle->iface, ifa);
2400   if (!ok) {
2401     if (arg->cmd->args)
2402       ok = 1;
2403     else if (arg->prompt)
2404       prompt_Printf(arg->prompt, "%s: No such address\n", inet_ntoa(ifa));
2405     else
2406       log_Printf(LogWARN, "%s: No such address\n", inet_ntoa(ifa));
2407   }
2408 
2409   return !ok;
2410 }
2411 
2412 static int
2413 IfaceClearCommand(struct cmdargs const *arg)
2414 {
2415   int how;
2416 
2417   if (arg->argc != arg->argn)
2418     return -1;
2419 
2420   how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ?
2421         IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
2422   iface_Clear(arg->bundle->iface, how);
2423 
2424   return 0;
2425 }
2426 
2427 static int
2428 SetProcTitle(struct cmdargs const *arg)
2429 {
2430   static char title[LINE_LEN];
2431   char *argv[MAXARGS], *ptr;
2432   int len, remaining, f, argc = arg->argc - arg->argn;
2433 
2434   if (arg->argc == arg->argn) {
2435     arg->bundle->argv[0] = arg->bundle->argv0;
2436     arg->bundle->argv[1] = arg->bundle->argv1;
2437     return 0;
2438   }
2439 
2440   if (argc >= sizeof argv / sizeof argv[0]) {
2441     argc = sizeof argv / sizeof argv[0] - 1;
2442     log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
2443   }
2444   expand(argv, argc, arg->argv + arg->argn, arg->bundle);
2445 
2446   ptr = title;
2447   remaining = sizeof title - 1;
2448   for (f = 0; f < argc && remaining; f++) {
2449     if (f) {
2450       *ptr++ = ' ';
2451       remaining--;
2452     }
2453     len = strlen(argv[f]);
2454     if (len > remaining)
2455       len = remaining;
2456     memcpy(ptr, argv[f], len);
2457     remaining -= len;
2458     ptr += len;
2459   }
2460   *ptr = '\0';
2461 
2462   arg->bundle->argv[0] = title;
2463   arg->bundle->argv[1] = NULL;
2464 
2465   return 0;
2466 }
2467