xref: /freebsd/usr.sbin/ppp/command.c (revision b64c5a0ace59af62eff52bfe110a521dc73c937b)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
5  *          based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
6  *                           Internet Initiative Japan, Inc (IIJ)
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30 
31 #include <sys/param.h>
32 #include <netinet/in_systm.h>
33 #include <netinet/in.h>
34 #include <netinet/ip.h>
35 #include <arpa/inet.h>
36 #include <sys/socket.h>
37 #include <net/route.h>
38 #include <netdb.h>
39 #include <sys/un.h>
40 
41 #include <ctype.h>
42 #include <errno.h>
43 #include <fcntl.h>
44 #include <paths.h>
45 #include <stdarg.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <sys/wait.h>
50 #include <termios.h>
51 #include <unistd.h>
52 
53 #ifndef NONAT
54 #ifdef LOCALNAT
55 #include "alias.h"
56 #else
57 #include <alias.h>
58 #endif
59 #endif
60 
61 #include "layer.h"
62 #include "defs.h"
63 #include "command.h"
64 #include "mbuf.h"
65 #include "log.h"
66 #include "timer.h"
67 #include "fsm.h"
68 #include "iplist.h"
69 #include "throughput.h"
70 #include "slcompress.h"
71 #include "lqr.h"
72 #include "hdlc.h"
73 #include "lcp.h"
74 #include "ncpaddr.h"
75 #include "ipcp.h"
76 #ifndef NONAT
77 #include "nat_cmd.h"
78 #endif
79 #include "systems.h"
80 #include "filter.h"
81 #include "descriptor.h"
82 #include "main.h"
83 #include "route.h"
84 #include "ccp.h"
85 #include "auth.h"
86 #include "async.h"
87 #include "link.h"
88 #include "physical.h"
89 #include "mp.h"
90 #ifndef NORADIUS
91 #include "radius.h"
92 #endif
93 #include "ipv6cp.h"
94 #include "ncp.h"
95 #include "bundle.h"
96 #include "server.h"
97 #include "prompt.h"
98 #include "chat.h"
99 #include "chap.h"
100 #include "cbcp.h"
101 #include "datalink.h"
102 #include "iface.h"
103 #include "id.h"
104 #include "probe.h"
105 
106 /* ``set'' values */
107 #define	VAR_AUTHKEY	0
108 #define	VAR_DIAL	1
109 #define	VAR_LOGIN	2
110 #define	VAR_AUTHNAME	3
111 #define	VAR_AUTOLOAD	4
112 #define	VAR_WINSIZE	5
113 #define	VAR_DEVICE	6
114 #define	VAR_ACCMAP	7
115 #define	VAR_MRRU	8
116 #define	VAR_MRU		9
117 #define	VAR_MTU		10
118 #define	VAR_OPENMODE	11
119 #define	VAR_PHONE	12
120 #define	VAR_HANGUP	13
121 #define	VAR_IDLETIMEOUT	14
122 #define	VAR_LQRPERIOD	15
123 #define	VAR_LCPRETRY	16
124 #define	VAR_CHAPRETRY	17
125 #define	VAR_PAPRETRY	18
126 #define	VAR_CCPRETRY	19
127 #define	VAR_IPCPRETRY	20
128 #define	VAR_DNS		21
129 #define	VAR_NBNS	22
130 #define	VAR_MODE	23
131 #define	VAR_CALLBACK	24
132 #define	VAR_CBCP	25
133 #define	VAR_CHOKED	26
134 #define	VAR_SENDPIPE	27
135 #define	VAR_RECVPIPE	28
136 #define	VAR_RADIUS	29
137 #define	VAR_CD		30
138 #define	VAR_PARITY	31
139 #define VAR_CRTSCTS	32
140 #define VAR_URGENT	33
141 #define	VAR_LOGOUT	34
142 #define	VAR_IFQUEUE	35
143 #define	VAR_MPPE	36
144 #define	VAR_IPV6CPRETRY	37
145 #define	VAR_RAD_ALIVE	38
146 #define	VAR_PPPOE	39
147 #define	VAR_PORT_ID	40
148 
149 /* ``accept|deny|disable|enable'' masks */
150 #define NEG_HISMASK (1)
151 #define NEG_MYMASK (2)
152 
153 /* ``accept|deny|disable|enable'' values */
154 #define NEG_ACFCOMP	40
155 #define NEG_CHAP05	41
156 #define NEG_CHAP80	42
157 #define NEG_CHAP80LM	43
158 #define NEG_DEFLATE	44
159 #define NEG_DNS		45
160 #define NEG_ECHO	46
161 #define NEG_ENDDISC	47
162 #define NEG_LQR		48
163 #define NEG_PAP		49
164 #define NEG_PPPDDEFLATE	50
165 #define NEG_PRED1	51
166 #define NEG_PROTOCOMP	52
167 #define NEG_SHORTSEQ	53
168 #define NEG_VJCOMP	54
169 #define NEG_MPPE	55
170 #define NEG_CHAP81	56
171 
172 const char Version[] = "3.4.2";
173 
174 static int ShowCommand(struct cmdargs const *);
175 static int TerminalCommand(struct cmdargs const *);
176 static int QuitCommand(struct cmdargs const *);
177 static int OpenCommand(struct cmdargs const *);
178 static int CloseCommand(struct cmdargs const *);
179 static int DownCommand(struct cmdargs const *);
180 static int SetCommand(struct cmdargs const *);
181 static int LinkCommand(struct cmdargs const *);
182 static int AddCommand(struct cmdargs const *);
183 static int DeleteCommand(struct cmdargs const *);
184 static int NegotiateCommand(struct cmdargs const *);
185 static int ClearCommand(struct cmdargs const *);
186 static int RunListCommand(struct cmdargs const *);
187 static int IfaceNameCommand(struct cmdargs const *arg);
188 static int IfaceAddCommand(struct cmdargs const *);
189 static int IfaceDeleteCommand(struct cmdargs const *);
190 static int IfaceClearCommand(struct cmdargs const *);
191 static int SetProcTitle(struct cmdargs const *);
192 #ifndef NONAT
193 static int NatEnable(struct cmdargs const *);
194 static int NatOption(struct cmdargs const *);
195 #endif
196 
197 extern struct libalias *la;
198 
199 static const char *
200 showcx(struct cmdtab const *cmd)
201 {
202   if (cmd->lauth & LOCAL_CX)
203     return "(c)";
204   else if (cmd->lauth & LOCAL_CX_OPT)
205     return "(o)";
206 
207   return "";
208 }
209 
210 static int
211 HelpCommand(struct cmdargs const *arg)
212 {
213   struct cmdtab const *cmd;
214   int n, cmax, dmax, cols, cxlen;
215   const char *cx;
216 
217   if (!arg->prompt) {
218     log_Printf(LogWARN, "help: Cannot help without a prompt\n");
219     return 0;
220   }
221 
222   if (arg->argc > arg->argn) {
223     for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
224       if ((cmd->lauth & arg->prompt->auth) &&
225           ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
226            (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
227 	prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
228 	return 0;
229       }
230     return -1;
231   }
232 
233   cmax = dmax = 0;
234   for (cmd = arg->cmdtab; cmd->func; cmd++)
235     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
236       if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
237         cmax = n;
238       if ((n = strlen(cmd->helpmes)) > dmax)
239         dmax = n;
240     }
241 
242   cols = 80 / (dmax + cmax + 3);
243   n = 0;
244   prompt_Printf(arg->prompt, "(o) = Optional context,"
245                 " (c) = Context required\n");
246   for (cmd = arg->cmdtab; cmd->func; cmd++)
247     if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
248       cx = showcx(cmd);
249       cxlen = cmax - strlen(cmd->name);
250       if (n % cols != 0)
251         prompt_Printf(arg->prompt, " ");
252       prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
253               cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
254       if (++n % cols == 0)
255         prompt_Printf(arg->prompt, "\n");
256     }
257   if (n % cols != 0)
258     prompt_Printf(arg->prompt, "\n");
259 
260   return 0;
261 }
262 
263 static int
264 IdentCommand(struct cmdargs const *arg)
265 {
266   Concatinate(arg->cx->physical->link.lcp.cfg.ident,
267               sizeof arg->cx->physical->link.lcp.cfg.ident,
268               arg->argc - arg->argn, arg->argv + arg->argn);
269   return 0;
270 }
271 
272 static int
273 SendIdentification(struct cmdargs const *arg)
274 {
275   if (arg->cx->state < DATALINK_LCP) {
276     log_Printf(LogWARN, "sendident: link has not reached LCP\n");
277     return 2;
278   }
279   return lcp_SendIdentification(&arg->cx->physical->link.lcp) ? 0 : 1;
280 }
281 
282 static int
283 CloneCommand(struct cmdargs const *arg)
284 {
285   char namelist[LINE_LEN];
286   char *name;
287   int f;
288 
289   if (arg->argc == arg->argn)
290     return -1;
291 
292   namelist[sizeof namelist - 1] = '\0';
293   for (f = arg->argn; f < arg->argc; f++) {
294     strncpy(namelist, arg->argv[f], sizeof namelist - 1);
295     for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
296       bundle_DatalinkClone(arg->bundle, arg->cx, name);
297   }
298 
299   return 0;
300 }
301 
302 static int
303 RemoveCommand(struct cmdargs const *arg)
304 {
305   if (arg->argc != arg->argn)
306     return -1;
307 
308   if (arg->cx->state != DATALINK_CLOSED) {
309     log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
310     return 2;
311   }
312 
313   bundle_DatalinkRemove(arg->bundle, arg->cx);
314   return 0;
315 }
316 
317 static int
318 RenameCommand(struct cmdargs const *arg)
319 {
320   if (arg->argc != arg->argn + 1)
321     return -1;
322 
323   if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
324     return 0;
325 
326   log_Printf(LogWARN, "%s -> %s: target name already exists\n",
327              arg->cx->name, arg->argv[arg->argn]);
328   return 1;
329 }
330 
331 static int
332 LoadCommand(struct cmdargs const *arg)
333 {
334   const char *err;
335   int n, mode;
336 
337   mode = arg->bundle->phys_type.all;
338 
339   if (arg->argn < arg->argc) {
340     for (n = arg->argn; n < arg->argc; n++)
341       if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
342         log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
343         return 1;
344       }
345 
346     for (n = arg->argn; n < arg->argc; n++) {
347       bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
348       system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
349     }
350     bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
351   } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
352     log_Printf(LogWARN, "default: %s\n", err);
353     return 1;
354   } else {
355     bundle_SetLabel(arg->bundle, "default");
356     system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
357     bundle_SetLabel(arg->bundle, "default");
358   }
359 
360   return 0;
361 }
362 
363 static int
364 LogCommand(struct cmdargs const *arg)
365 {
366   char buf[LINE_LEN];
367 
368   if (arg->argn < arg->argc) {
369     char *argv[MAXARGS];
370     int argc = arg->argc - arg->argn;
371 
372     if (argc >= (int)(sizeof argv / sizeof argv[0])) {
373       argc = sizeof argv / sizeof argv[0] - 1;
374       log_Printf(LogWARN, "Truncating log command to %d args\n", argc);
375     }
376     command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
377     Concatinate(buf, sizeof buf, argc, (const char *const *)argv);
378     log_Printf(LogLOG, "%s\n", buf);
379     command_Free(argc, argv);
380     return 0;
381   }
382 
383   return -1;
384 }
385 
386 static int
387 SaveCommand(struct cmdargs const *arg __unused)
388 {
389   log_Printf(LogWARN, "save command is not yet implemented.\n");
390   return 1;
391 }
392 
393 static int
394 DialCommand(struct cmdargs const *arg)
395 {
396   int res;
397 
398   if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
399       || (!arg->cx &&
400           (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
401     log_Printf(LogWARN, "Manual dial is only available for auto and"
402               " interactive links\n");
403     return 1;
404   }
405 
406   if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
407     return res;
408 
409   bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
410 
411   return 0;
412 }
413 
414 #define isinword(ch) (isalnum(ch) || (ch) == '_')
415 
416 static char *
417 strstrword(char *big, const char *little)
418 {
419   /* Get the first occurrence of the word ``little'' in ``big'' */
420   char *pos;
421   int len;
422 
423   pos = big;
424   len = strlen(little);
425 
426   while ((pos = strstr(pos, little)) != NULL)
427     if ((pos != big && isinword(pos[-1])) || isinword(pos[len]))
428       pos++;
429     else if (pos != big && pos[-1] == '\\')
430       memmove(pos - 1, pos, strlen(pos) + 1);
431     else
432       break;
433 
434   return pos;
435 }
436 
437 static char *
438 subst(char *tgt, const char *oldstr, const char *newstr)
439 {
440   /* tgt is a malloc()d area... realloc() as necessary */
441   char *word, *ntgt;
442   int ltgt, loldstr, lnewstr, pos;
443 
444   if ((word = strstrword(tgt, oldstr)) == NULL)
445     return tgt;
446 
447   ltgt = strlen(tgt) + 1;
448   loldstr = strlen(oldstr);
449   lnewstr = strlen(newstr);
450   do {
451     pos = word - tgt;
452     if (loldstr > lnewstr)
453       bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
454     if (loldstr != lnewstr) {
455       ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
456       if (ntgt == NULL)
457         break;			/* Oh wonderful ! */
458       word = ntgt + pos;
459       tgt = ntgt;
460     }
461     if (lnewstr > loldstr)
462       bcopy(word + loldstr, word + lnewstr, ltgt - pos - lnewstr);
463     bcopy(newstr, word, lnewstr);
464   } while ((word = strstrword(word, oldstr)));
465 
466   return tgt;
467 }
468 
469 static char *
470 substip(char *tgt, const char *oldstr, struct in_addr ip)
471 {
472   return subst(tgt, oldstr, inet_ntoa(ip));
473 }
474 
475 static char *
476 substlong(char *tgt, const char *oldstr, long l)
477 {
478   char buf[23];
479 
480   snprintf(buf, sizeof buf, "%ld", l);
481 
482   return subst(tgt, oldstr, buf);
483 }
484 
485 static char *
486 substull(char *tgt, const char *oldstr, unsigned long long ull)
487 {
488   char buf[21];
489 
490   snprintf(buf, sizeof buf, "%llu", ull);
491 
492   return subst(tgt, oldstr, buf);
493 }
494 
495 
496 #ifndef NOINET6
497 static char *
498 substipv6(char *tgt, const char *oldstr, const struct ncpaddr *ip)
499 {
500     return subst(tgt, oldstr, ncpaddr_ntoa(ip));
501 }
502 
503 #ifndef NORADIUS
504 static char *
505 substipv6prefix(char *tgt, const char *oldstr, const uint8_t *ipv6prefix)
506 {
507   uint8_t ipv6addr[INET6_ADDRSTRLEN];
508   uint8_t prefix[INET6_ADDRSTRLEN + sizeof("/128") - 1];
509 
510   if (ipv6prefix) {
511     inet_ntop(AF_INET6, &ipv6prefix[2], ipv6addr, sizeof(ipv6addr));
512     snprintf(prefix, sizeof(prefix), "%s/%d", ipv6addr, ipv6prefix[1]);
513   } else
514     prefix[0] = '\0';
515   return subst(tgt, oldstr, prefix);
516 }
517 #endif
518 #endif
519 
520 void
521 command_Expand(char **nargv, int argc, char const *const *oargv,
522                struct bundle *bundle, int inc0, pid_t pid)
523 {
524   int arg, secs;
525   char uptime[20];
526   unsigned long long oin, oout, pin, pout;
527 
528   if (inc0)
529     arg = 0;		/* Start at arg 0 */
530   else {
531     nargv[0] = strdup(oargv[0]);
532     arg = 1;
533   }
534 
535   secs = bundle_Uptime(bundle);
536   snprintf(uptime, sizeof uptime, "%d:%02d:%02d",
537            secs / 3600, (secs / 60) % 60, secs % 60);
538   oin = bundle->ncp.ipcp.throughput.OctetsIn;
539   oout = bundle->ncp.ipcp.throughput.OctetsOut;
540   pin = bundle->ncp.ipcp.throughput.PacketsIn;
541   pout = bundle->ncp.ipcp.throughput.PacketsOut;
542 #ifndef NOINET6
543   oin += bundle->ncp.ipv6cp.throughput.OctetsIn;
544   oout += bundle->ncp.ipv6cp.throughput.OctetsOut;
545   pin += bundle->ncp.ipv6cp.throughput.PacketsIn;
546   pout += bundle->ncp.ipv6cp.throughput.PacketsOut;
547 #endif
548 
549   for (; arg < argc; arg++) {
550     nargv[arg] = strdup(oargv[arg]);
551     nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
552     nargv[arg] = substip(nargv[arg], "DNS0", bundle->ncp.ipcp.ns.dns[0]);
553     nargv[arg] = substip(nargv[arg], "DNS1", bundle->ncp.ipcp.ns.dns[1]);
554     nargv[arg] = subst(nargv[arg], "ENDDISC",
555                        mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
556                                   bundle->ncp.mp.cfg.enddisc.address,
557                                   bundle->ncp.mp.cfg.enddisc.len));
558     nargv[arg] = substip(nargv[arg], "HISADDR", bundle->ncp.ipcp.peer_ip);
559 #ifndef NOINET6
560     nargv[arg] = substipv6(nargv[arg], "HISADDR6", &bundle->ncp.ipv6cp.hisaddr);
561 #endif
562     nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
563     nargv[arg] = substull(nargv[arg], "IPOCTETSIN",
564                           bundle->ncp.ipcp.throughput.OctetsIn);
565     nargv[arg] = substull(nargv[arg], "IPOCTETSOUT",
566                           bundle->ncp.ipcp.throughput.OctetsOut);
567     nargv[arg] = substull(nargv[arg], "IPPACKETSIN",
568                           bundle->ncp.ipcp.throughput.PacketsIn);
569     nargv[arg] = substull(nargv[arg], "IPPACKETSOUT",
570                           bundle->ncp.ipcp.throughput.PacketsOut);
571 #ifndef NOINET6
572     nargv[arg] = substull(nargv[arg], "IPV6OCTETSIN",
573                           bundle->ncp.ipv6cp.throughput.OctetsIn);
574     nargv[arg] = substull(nargv[arg], "IPV6OCTETSOUT",
575                           bundle->ncp.ipv6cp.throughput.OctetsOut);
576     nargv[arg] = substull(nargv[arg], "IPV6PACKETSIN",
577                           bundle->ncp.ipv6cp.throughput.PacketsIn);
578     nargv[arg] = substull(nargv[arg], "IPV6PACKETSOUT",
579                           bundle->ncp.ipv6cp.throughput.PacketsOut);
580 #endif
581     nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
582     nargv[arg] = substip(nargv[arg], "MYADDR", bundle->ncp.ipcp.my_ip);
583 #ifndef NOINET6
584     nargv[arg] = substipv6(nargv[arg], "MYADDR6", &bundle->ncp.ipv6cp.myaddr);
585 #ifndef NORADIUS
586     nargv[arg] = substipv6prefix(nargv[arg], "IPV6PREFIX",
587 				 bundle->radius.ipv6prefix);
588 #endif
589 #endif
590     nargv[arg] = substull(nargv[arg], "OCTETSIN", oin);
591     nargv[arg] = substull(nargv[arg], "OCTETSOUT", oout);
592     nargv[arg] = substull(nargv[arg], "PACKETSIN", pin);
593     nargv[arg] = substull(nargv[arg], "PACKETSOUT", pout);
594     nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
595                        mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
596                                   bundle->ncp.mp.peer.enddisc.address,
597                                   bundle->ncp.mp.peer.enddisc.len));
598     nargv[arg] = substlong(nargv[arg], "PROCESSID", pid);
599     if (server.cfg.port)
600       nargv[arg] = substlong(nargv[arg], "SOCKNAME", server.cfg.port);
601     else
602       nargv[arg] = subst(nargv[arg], "SOCKNAME", server.cfg.sockname);
603     nargv[arg] = subst(nargv[arg], "UPTIME", uptime);
604     nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
605     nargv[arg] = subst(nargv[arg], "VERSION", Version);
606   }
607   nargv[arg] = NULL;
608 }
609 
610 void
611 command_Free(int argc, char **argv)
612 {
613   while (argc) {
614     free(*argv);
615     argc--;
616     argv++;
617   }
618 }
619 
620 static int
621 ShellCommand(struct cmdargs const *arg, int bg)
622 {
623   const char *shell;
624   pid_t shpid, pid;
625 
626 #ifdef SHELL_ONLY_INTERACTIVELY
627   /* we're only allowed to shell when we run ppp interactively */
628   if (arg->prompt && arg->prompt->owner) {
629     log_Printf(LogWARN, "Can't start a shell from a network connection\n");
630     return 1;
631   }
632 #endif
633 
634   if (arg->argc == arg->argn) {
635     if (!arg->prompt) {
636       log_Printf(LogWARN, "Can't start an interactive shell from"
637                 " a config file\n");
638       return 1;
639     } else if (arg->prompt->owner) {
640       log_Printf(LogWARN, "Can't start an interactive shell from"
641                 " a socket connection\n");
642       return 1;
643     } else if (bg) {
644       log_Printf(LogWARN, "Can only start an interactive shell in"
645 		" the foreground mode\n");
646       return 1;
647     }
648   }
649 
650   pid = getpid();
651   if ((shpid = fork()) == 0) {
652     int i, fd;
653 
654     if ((shell = getenv("SHELL")) == NULL)
655       shell = _PATH_BSHELL;
656 
657     timer_TermService();
658 
659     if (arg->prompt)
660       fd = arg->prompt->fd_out;
661     else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
662       log_Printf(LogALERT, "Failed to open %s: %s\n",
663                 _PATH_DEVNULL, strerror(errno));
664       exit(1);
665     }
666     dup2(fd, STDIN_FILENO);
667     dup2(fd, STDOUT_FILENO);
668     dup2(fd, STDERR_FILENO);
669     for (i = getdtablesize(); i > STDERR_FILENO; i--)
670       fcntl(i, F_SETFD, 1);
671 
672 #ifndef NOSUID
673     setuid(ID0realuid());
674 #endif
675     if (arg->argc > arg->argn) {
676       /* substitute pseudo args */
677       char *argv[MAXARGS];
678       int argc = arg->argc - arg->argn;
679 
680       if (argc >= (int)(sizeof argv / sizeof argv[0])) {
681         argc = sizeof argv / sizeof argv[0] - 1;
682         log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
683       }
684       command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0, pid);
685       if (bg) {
686 	pid_t p;
687 
688 	p = getpid();
689 	if (daemon(1, 1) == -1) {
690 	  log_Printf(LogERROR, "%ld: daemon: %s\n", (long)p, strerror(errno));
691 	  exit(1);
692 	}
693       } else if (arg->prompt)
694         printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
695       execvp(argv[0], argv);
696     } else {
697       if (arg->prompt)
698         printf("ppp: Pausing until %s finishes\n", shell);
699       prompt_TtyOldMode(arg->prompt);
700       execl(shell, shell, (char *)NULL);
701     }
702 
703     log_Printf(LogWARN, "exec() of %s failed: %s\n",
704               arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
705               strerror(errno));
706     _exit(255);
707   }
708 
709   if (shpid == (pid_t)-1)
710     log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
711   else {
712     int status;
713     waitpid(shpid, &status, 0);
714   }
715 
716   if (arg->prompt && !arg->prompt->owner)
717     prompt_TtyCommandMode(arg->prompt);
718 
719   return 0;
720 }
721 
722 static int
723 BgShellCommand(struct cmdargs const *arg)
724 {
725   if (arg->argc == arg->argn)
726     return -1;
727   return ShellCommand(arg, 1);
728 }
729 
730 static int
731 FgShellCommand(struct cmdargs const *arg)
732 {
733   return ShellCommand(arg, 0);
734 }
735 
736 static int
737 ResolvCommand(struct cmdargs const *arg)
738 {
739   if (arg->argc == arg->argn + 1) {
740     if (!strcasecmp(arg->argv[arg->argn], "reload"))
741       ipcp_LoadDNS(&arg->bundle->ncp.ipcp);
742     else if (!strcasecmp(arg->argv[arg->argn], "restore"))
743       ipcp_RestoreDNS(&arg->bundle->ncp.ipcp);
744     else if (!strcasecmp(arg->argv[arg->argn], "rewrite"))
745       ipcp_WriteDNS(&arg->bundle->ncp.ipcp);
746     else if (!strcasecmp(arg->argv[arg->argn], "readonly"))
747       arg->bundle->ncp.ipcp.ns.writable = 0;
748     else if (!strcasecmp(arg->argv[arg->argn], "writable"))
749       arg->bundle->ncp.ipcp.ns.writable = 1;
750     else
751       return -1;
752 
753     return 0;
754   }
755 
756   return -1;
757 }
758 
759 #ifndef NONAT
760 static struct cmdtab const NatCommands[] =
761 {
762   {"addr", NULL, nat_RedirectAddr, LOCAL_AUTH,
763    "static address translation", "nat addr [addr_local addr_alias]", NULL},
764   {"deny_incoming", NULL, NatOption, LOCAL_AUTH,
765    "stop incoming connections", "nat deny_incoming yes|no",
766    (const void *) PKT_ALIAS_DENY_INCOMING},
767   {"enable", NULL, NatEnable, LOCAL_AUTH,
768    "enable NAT", "nat enable yes|no", NULL},
769   {"log", NULL, NatOption, LOCAL_AUTH,
770    "log NAT link creation", "nat log yes|no",
771    (const void *) PKT_ALIAS_LOG},
772   {"port", NULL, nat_RedirectPort, LOCAL_AUTH, "port redirection",
773    "nat port proto localaddr:port[-port] aliasport[-aliasport]", NULL},
774   {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
775    "nat proto proto localIP [publicIP [remoteIP]]", NULL},
776   {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
777    "proxy control", "nat proxy server host[:port] ...", NULL},
778 #ifndef NO_FW_PUNCH
779   {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
780    "firewall control", "nat punch_fw [base count]", NULL},
781 #endif
782   {"skinny_port", NULL, nat_SkinnyPort, LOCAL_AUTH,
783    "TCP port used by Skinny Station protocol", "nat skinny_port [port]", NULL},
784   {"same_ports", NULL, NatOption, LOCAL_AUTH,
785    "try to leave port numbers unchanged", "nat same_ports yes|no",
786    (const void *) PKT_ALIAS_SAME_PORTS},
787   {"target", NULL, nat_SetTarget, LOCAL_AUTH,
788    "Default address for incoming connections", "nat target addr", NULL},
789   {"unregistered_only", NULL, NatOption, LOCAL_AUTH,
790    "translate unregistered (private) IP address space only",
791    "nat unregistered_only yes|no",
792    (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
793   {"use_sockets", NULL, NatOption, LOCAL_AUTH,
794    "allocate host sockets", "nat use_sockets yes|no",
795    (const void *) PKT_ALIAS_USE_SOCKETS},
796   {"udp_eim", NULL, NatOption, LOCAL_AUTH,
797    "UDP uses endpoint-independent mapping (\"full cone\" NAT)",
798    "nat udp_eim yes|no",
799    (const void *) PKT_ALIAS_UDP_EIM},
800   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
801    "Display this message", "nat help|? [command]", NatCommands},
802   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
803 };
804 #endif
805 
806 static struct cmdtab const AllowCommands[] = {
807   {"modes", "mode", AllowModes, LOCAL_AUTH,
808   "Only allow certain ppp modes", "allow modes mode...", NULL},
809   {"users", "user", AllowUsers, LOCAL_AUTH,
810   "Only allow ppp access to certain users", "allow users logname...", NULL},
811   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
812   "Display this message", "allow help|? [command]", AllowCommands},
813   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
814 };
815 
816 static struct cmdtab const IfaceCommands[] =
817 {
818   {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
819    "Add iface address", "iface add addr[/bits| mask] peer", NULL},
820   {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
821    "Add or change an iface address", "iface add! addr[/bits| mask] peer",
822    (void *)1},
823   {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
824    "Clear iface address(es)", "iface clear [INET | INET6]", NULL},
825   {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
826    "Delete iface address", "iface delete addr", NULL},
827   {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
828    "Delete iface address", "iface delete addr", (void *)1},
829   {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
830    "Delete iface address", "iface delete addr", (void *)1},
831   {"name", NULL, IfaceNameCommand, LOCAL_AUTH,
832     "Set iface name", "iface name name", NULL},
833   {"description", NULL, iface_Descr, LOCAL_AUTH,
834     "Set iface description", "iface description text", NULL},
835   {"show", NULL, iface_Show, LOCAL_AUTH,
836    "Show iface address(es)", "iface show", NULL},
837   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
838    "Display this message", "nat help|? [command]", IfaceCommands},
839   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
840 };
841 
842 static struct cmdtab const Commands[] = {
843   {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
844   "accept option request", "accept option ..", NULL},
845   {"add", NULL, AddCommand, LOCAL_AUTH,
846   "add route", "add dest mask gateway", NULL},
847   {NULL, "add!", AddCommand, LOCAL_AUTH,
848   "add or change route", "add! dest mask gateway", (void *)1},
849   {"allow", "auth", RunListCommand, LOCAL_AUTH,
850   "Allow ppp access", "allow users|modes ....", AllowCommands},
851   {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
852   "Run a background command", "[!]bg command", NULL},
853   {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
854   "Clear throughput statistics",
855   "clear ipcp|ipv6cp|physical [current|overall|peak]...", NULL},
856   {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
857   "Clone a link", "clone newname...", NULL},
858   {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
859   "Close an FSM", "close [lcp|ccp]", NULL},
860   {"delete", NULL, DeleteCommand, LOCAL_AUTH,
861   "delete route", "delete dest", NULL},
862   {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
863   "delete a route if it exists", "delete! dest", (void *)1},
864   {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
865   "Deny option request", "deny option ..", NULL},
866   {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
867   "Dial and login", "dial|call [system ...]", NULL},
868   {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
869   "Disable option", "disable option ..", NULL},
870   {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
871   "Generate a down event", "down [ccp|lcp]", NULL},
872   {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
873   "Enable option", "enable option ..", NULL},
874   {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
875   "Set the link identity", "ident text...", NULL},
876   {"iface", "interface", RunListCommand, LOCAL_AUTH,
877   "interface control", "iface option ...", IfaceCommands},
878   {"link", "datalink", LinkCommand, LOCAL_AUTH,
879   "Link specific commands", "link name command ...", NULL},
880   {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
881   "Load settings", "load [system ...]", NULL},
882   {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
883   "log information", "log word ...", NULL},
884 #ifndef NONAT
885   {"nat", "alias", RunListCommand, LOCAL_AUTH,
886   "NAT control", "nat option yes|no", NatCommands},
887 #endif
888   {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
889   "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
890   {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
891   "Password for manipulation", "passwd LocalPassword", NULL},
892   {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
893   "Quit PPP program", "quit|bye [all]", NULL},
894   {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
895   "Remove a link", "remove", NULL},
896   {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
897   "Rename a link", "rename name", NULL},
898   {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
899   "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable",
900   NULL},
901   {"save", NULL, SaveCommand, LOCAL_AUTH,
902   "Save settings", "save", NULL},
903   {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
904   "Transmit the link identity", "sendident", NULL},
905   {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
906   "Set parameters", "set[up] var value", NULL},
907   {"shell", "!", FgShellCommand, LOCAL_AUTH,
908   "Run a subshell", "shell|! [sh command]", NULL},
909   {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
910   "Show status and stats", "show var", NULL},
911   {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
912   "Enter terminal mode", "term", NULL},
913   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
914   "Display this message", "help|? [command]", Commands},
915   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
916 };
917 
918 static int
919 ShowEscape(struct cmdargs const *arg)
920 {
921   if (arg->cx->physical->async.cfg.EscMap[32]) {
922     int code, bit;
923     const char *sep = "";
924 
925     for (code = 0; code < 32; code++)
926       if (arg->cx->physical->async.cfg.EscMap[code])
927 	for (bit = 0; bit < 8; bit++)
928 	  if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
929 	    prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
930             sep = ", ";
931           }
932     prompt_Printf(arg->prompt, "\n");
933   }
934   return 0;
935 }
936 
937 static int
938 ShowTimerList(struct cmdargs const *arg)
939 {
940   timer_Show(0, arg->prompt);
941   return 0;
942 }
943 
944 static int
945 ShowStopped(struct cmdargs const *arg)
946 {
947   prompt_Printf(arg->prompt, " Stopped Timer:  LCP: ");
948   if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
949     prompt_Printf(arg->prompt, "Disabled");
950   else
951     prompt_Printf(arg->prompt, "%ld secs",
952                   arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
953 
954   prompt_Printf(arg->prompt, ", CCP: ");
955   if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
956     prompt_Printf(arg->prompt, "Disabled");
957   else
958     prompt_Printf(arg->prompt, "%ld secs",
959                   arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
960 
961   prompt_Printf(arg->prompt, "\n");
962 
963   return 0;
964 }
965 
966 static int
967 ShowVersion(struct cmdargs const *arg)
968 {
969   prompt_Printf(arg->prompt, "PPP Version %s\n", Version);
970   return 0;
971 }
972 
973 static int
974 ShowProtocolStats(struct cmdargs const *arg)
975 {
976   struct link *l = command_ChooseLink(arg);
977 
978   prompt_Printf(arg->prompt, "%s:\n", l->name);
979   link_ReportProtocolStatus(l, arg->prompt);
980   return 0;
981 }
982 
983 static struct cmdtab const ShowCommands[] = {
984   {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
985   "bundle details", "show bundle", NULL},
986   {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
987   "CCP status", "show cpp", NULL},
988   {"compress", NULL, sl_Show, LOCAL_AUTH,
989   "VJ compression stats", "show compress", NULL},
990   {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
991   "escape characters", "show escape", NULL},
992   {"filter", NULL, filter_Show, LOCAL_AUTH,
993   "packet filters", "show filter [in|out|dial|alive]", NULL},
994   {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
995   "HDLC errors", "show hdlc", NULL},
996   {"iface", "interface", iface_Show, LOCAL_AUTH,
997   "Interface status", "show iface", NULL},
998   {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
999   "IPCP status", "show ipcp", NULL},
1000 #ifndef NOINET6
1001   {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
1002   "IPV6CP status", "show ipv6cp", NULL},
1003 #endif
1004   {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
1005   "Protocol layers", "show layers", NULL},
1006   {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
1007   "LCP status", "show lcp", NULL},
1008   {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
1009   "(high-level) link info", "show link", NULL},
1010   {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
1011   "available link names", "show links", NULL},
1012   {"log", NULL, log_ShowLevel, LOCAL_AUTH,
1013   "log levels", "show log", NULL},
1014   {"mem", NULL, mbuf_Show, LOCAL_AUTH,
1015   "mbuf allocations", "show mem", NULL},
1016   {"ncp", NULL, ncp_Show, LOCAL_AUTH,
1017   "NCP status", "show ncp", NULL},
1018   {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
1019   "(low-level) link info", "show physical", NULL},
1020   {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
1021   "multilink setup", "show mp", NULL},
1022   {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
1023   "protocol summary", "show proto", NULL},
1024   {"route", NULL, route_Show, LOCAL_AUTH,
1025   "routing table", "show route", NULL},
1026   {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
1027   "STOPPED timeout", "show stopped", NULL},
1028   {"timers", NULL, ShowTimerList, LOCAL_AUTH,
1029   "alarm timers", "show timers", NULL},
1030   {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
1031   "version string", "show version", NULL},
1032   {"who", NULL, log_ShowWho, LOCAL_AUTH,
1033   "client list", "show who", NULL},
1034   {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
1035   "Display this message", "show help|? [command]", ShowCommands},
1036   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
1037 };
1038 
1039 static struct cmdtab const *
1040 FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
1041 {
1042   int nmatch;
1043   int len;
1044   struct cmdtab const *found;
1045 
1046   found = NULL;
1047   len = strlen(str);
1048   nmatch = 0;
1049   while (cmds->func) {
1050     if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
1051       if (cmds->name[len] == '\0') {
1052 	*pmatch = 1;
1053 	return cmds;
1054       }
1055       nmatch++;
1056       found = cmds;
1057     } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
1058       if (cmds->alias[len] == '\0') {
1059 	*pmatch = 1;
1060 	return cmds;
1061       }
1062       nmatch++;
1063       found = cmds;
1064     }
1065     cmds++;
1066   }
1067   *pmatch = nmatch;
1068   return found;
1069 }
1070 
1071 static const char *
1072 mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
1073 {
1074   int f, tlen, len;
1075 
1076   tlen = 0;
1077   for (f = 0; f < argc && tlen < sz - 2; f++) {
1078     if (f)
1079       tgt[tlen++] = ' ';
1080     len = strlen(argv[f]);
1081     if (len > sz - tlen - 1)
1082       len = sz - tlen - 1;
1083     strncpy(tgt+tlen, argv[f], len);
1084     tlen += len;
1085   }
1086   tgt[tlen] = '\0';
1087   return tgt;
1088 }
1089 
1090 static int
1091 FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
1092          char const *const *argv, struct prompt *prompt, struct datalink *cx)
1093 {
1094   struct cmdtab const *cmd;
1095   int val = 1;
1096   int nmatch;
1097   struct cmdargs arg;
1098   char prefix[100];
1099 
1100   cmd = FindCommand(cmds, argv[argn], &nmatch);
1101   if (nmatch > 1)
1102     log_Printf(LogWARN, "%s: Ambiguous command\n",
1103               mkPrefix(argn+1, argv, prefix, sizeof prefix));
1104   else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
1105     if ((cmd->lauth & LOCAL_CX) && !cx)
1106       /* We've got no context, but we require it */
1107       cx = bundle2datalink(bundle, NULL);
1108 
1109     if ((cmd->lauth & LOCAL_CX) && !cx)
1110       log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
1111                 mkPrefix(argn+1, argv, prefix, sizeof prefix));
1112     else {
1113       if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1114         log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
1115                   mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
1116         cx = NULL;
1117       }
1118       arg.cmdtab = cmds;
1119       arg.cmd = cmd;
1120       arg.argc = argc;
1121       arg.argn = argn+1;
1122       arg.argv = argv;
1123       arg.bundle = bundle;
1124       arg.cx = cx;
1125       arg.prompt = prompt;
1126       val = (*cmd->func) (&arg);
1127     }
1128   } else
1129     log_Printf(LogWARN, "%s: Invalid command\n",
1130               mkPrefix(argn+1, argv, prefix, sizeof prefix));
1131 
1132   if (val == -1)
1133     log_Printf(LogWARN, "usage: %s\n", cmd->syntax);
1134   else if (val)
1135     log_Printf(LogWARN, "%s: Failed %d\n",
1136               mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
1137 
1138   return val;
1139 }
1140 
1141 int
1142 command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset)
1143 {
1144   char buff2[LINE_LEN-offset];
1145 
1146   InterpretArg(buff, buff2);
1147   strncpy(buff, buff2, LINE_LEN - offset - 1);
1148   buff[LINE_LEN - offset - 1] = '\0';
1149 
1150   return command_Interpret(buff, nb, argv);
1151 }
1152 
1153 int
1154 command_Interpret(char *buff, int nb, char *argv[MAXARGS])
1155 {
1156   char *cp;
1157 
1158   if (nb > 0) {
1159     cp = buff + strcspn(buff, "\r\n");
1160     if (cp)
1161       *cp = '\0';
1162     return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE);
1163   }
1164   return 0;
1165 }
1166 
1167 static int
1168 arghidden(char const *const *argv, int n)
1169 {
1170   /* Is arg n of the given command to be hidden from the log ? */
1171 
1172   /* set authkey xxxxx */
1173   /* set key xxxxx */
1174   if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
1175       (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
1176     return 1;
1177 
1178   /* passwd xxxxx */
1179   if (n == 1 && !strncasecmp(argv[0], "p", 1))
1180     return 1;
1181 
1182   /* set server port xxxxx .... */
1183   if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
1184       !strncasecmp(argv[1], "se", 2))
1185     return 1;
1186 
1187   return 0;
1188 }
1189 
1190 void
1191 command_Run(struct bundle *bundle, int argc, char const *const *argv,
1192            struct prompt *prompt, const char *label, struct datalink *cx)
1193 {
1194   if (argc > 0) {
1195     if (log_IsKept(LogCOMMAND)) {
1196       char buf[LINE_LEN];
1197       int f;
1198       size_t n;
1199 
1200       if (label) {
1201         strncpy(buf, label, sizeof buf - 3);
1202         buf[sizeof buf - 3] = '\0';
1203         strcat(buf, ": ");
1204         n = strlen(buf);
1205       } else {
1206         *buf = '\0';
1207         n = 0;
1208       }
1209       buf[sizeof buf - 1] = '\0';	/* In case we run out of room in buf */
1210 
1211       for (f = 0; f < argc; f++) {
1212         if (n < sizeof buf - 1 && f)
1213           buf[n++] = ' ';
1214         if (arghidden(argv, f))
1215           strncpy(buf+n, "********", sizeof buf - n - 1);
1216         else
1217           strncpy(buf+n, argv[f], sizeof buf - n - 1);
1218         n += strlen(buf+n);
1219       }
1220       log_Printf(LogCOMMAND, "%s\n", buf);
1221     }
1222     FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
1223   }
1224 }
1225 
1226 int
1227 command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
1228               const char *label)
1229 {
1230   int argc;
1231   char *argv[MAXARGS];
1232 
1233   if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0)
1234     return 0;
1235 
1236   command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
1237   return 1;
1238 }
1239 
1240 static int
1241 ShowCommand(struct cmdargs const *arg)
1242 {
1243   if (!arg->prompt)
1244     log_Printf(LogWARN, "show: Cannot show without a prompt\n");
1245   else if (arg->argc > arg->argn)
1246     FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
1247              arg->prompt, arg->cx);
1248   else
1249     prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
1250 
1251   return 0;
1252 }
1253 
1254 static int
1255 TerminalCommand(struct cmdargs const *arg)
1256 {
1257   if (!arg->prompt) {
1258     log_Printf(LogWARN, "term: Need a prompt\n");
1259     return 1;
1260   }
1261 
1262   if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
1263     prompt_Printf(arg->prompt, "LCP state is [%s]\n",
1264                   State2Nam(arg->cx->physical->link.lcp.fsm.state));
1265     return 1;
1266   }
1267 
1268   datalink_Up(arg->cx, 0, 0);
1269   prompt_TtyTermMode(arg->prompt, arg->cx);
1270   return 0;
1271 }
1272 
1273 static int
1274 QuitCommand(struct cmdargs const *arg)
1275 {
1276   if (!arg->prompt || prompt_IsController(arg->prompt) ||
1277       (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
1278        (arg->prompt->auth & LOCAL_AUTH)))
1279     Cleanup();
1280   if (arg->prompt)
1281     prompt_Destroy(arg->prompt, 1);
1282 
1283   return 0;
1284 }
1285 
1286 static int
1287 OpenCommand(struct cmdargs const *arg)
1288 {
1289   if (arg->argc == arg->argn)
1290     bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
1291   else if (arg->argc == arg->argn + 1) {
1292     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1293       struct datalink *cx = arg->cx ?
1294         arg->cx : bundle2datalink(arg->bundle, NULL);
1295       if (cx) {
1296         if (cx->physical->link.lcp.fsm.state == ST_OPENED)
1297           fsm_Reopen(&cx->physical->link.lcp.fsm);
1298         else
1299           bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
1300       } else
1301         log_Printf(LogWARN, "open lcp: You must specify a link\n");
1302     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1303       struct fsm *fp;
1304 
1305       fp = &command_ChooseLink(arg)->ccp.fsm;
1306       if (fp->link->lcp.fsm.state != ST_OPENED)
1307         log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
1308       else if (fp->state == ST_OPENED)
1309         fsm_Reopen(fp);
1310       else {
1311         fp->open_mode = 0;	/* Not passive any more */
1312         if (fp->state == ST_STOPPED) {
1313           fsm_Down(fp);
1314           fsm_Up(fp);
1315         } else {
1316           fsm_Up(fp);
1317           fsm_Open(fp);
1318         }
1319       }
1320     } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
1321       if (arg->cx)
1322         log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
1323       if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
1324         fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
1325       else
1326         bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
1327     } else
1328       return -1;
1329   } else
1330     return -1;
1331 
1332   return 0;
1333 }
1334 
1335 static int
1336 CloseCommand(struct cmdargs const *arg)
1337 {
1338   if (arg->argc == arg->argn)
1339     bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
1340   else if (arg->argc == arg->argn + 1) {
1341     if (!strcasecmp(arg->argv[arg->argn], "lcp"))
1342       bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
1343     else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
1344              !strcasecmp(arg->argv[arg->argn], "ccp!")) {
1345       struct fsm *fp;
1346 
1347       fp = &command_ChooseLink(arg)->ccp.fsm;
1348       if (fp->state == ST_OPENED) {
1349         fsm_Close(fp);
1350         if (arg->argv[arg->argn][3] == '!')
1351           fp->open_mode = 0;		/* Stay ST_CLOSED */
1352         else
1353           fp->open_mode = OPEN_PASSIVE;	/* Wait for the peer to start */
1354       }
1355     } else
1356       return -1;
1357   } else
1358     return -1;
1359 
1360   return 0;
1361 }
1362 
1363 static int
1364 DownCommand(struct cmdargs const *arg)
1365 {
1366   if (arg->argc == arg->argn) {
1367       if (arg->cx)
1368         datalink_Down(arg->cx, CLOSE_STAYDOWN);
1369       else
1370         bundle_Down(arg->bundle, CLOSE_STAYDOWN);
1371   } else if (arg->argc == arg->argn + 1) {
1372     if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
1373       if (arg->cx)
1374         datalink_Down(arg->cx, CLOSE_LCP);
1375       else
1376         bundle_Down(arg->bundle, CLOSE_LCP);
1377     } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
1378       struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
1379                                  &arg->bundle->ncp.mp.link.ccp.fsm;
1380       fsm2initial(fp);
1381     } else
1382       return -1;
1383   } else
1384     return -1;
1385 
1386   return 0;
1387 }
1388 
1389 static int
1390 SetModemSpeed(struct cmdargs const *arg)
1391 {
1392   long speed;
1393   char *end;
1394 
1395   if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
1396     if (arg->argc > arg->argn+1) {
1397       log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n");
1398       return -1;
1399     }
1400     if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
1401       physical_SetSync(arg->cx->physical);
1402       return 0;
1403     }
1404     end = NULL;
1405     speed = strtol(arg->argv[arg->argn], &end, 10);
1406     if (*end || speed < 0) {
1407       log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
1408                 arg->argv[arg->argn]);
1409       return -1;
1410     }
1411     if (physical_SetSpeed(arg->cx->physical, speed))
1412       return 0;
1413     log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
1414   } else
1415     log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
1416 
1417   return -1;
1418 }
1419 
1420 static int
1421 SetStoppedTimeout(struct cmdargs const *arg)
1422 {
1423   struct link *l = &arg->cx->physical->link;
1424 
1425   l->lcp.fsm.StoppedTimer.load = 0;
1426   l->ccp.fsm.StoppedTimer.load = 0;
1427   if (arg->argc <= arg->argn+2) {
1428     if (arg->argc > arg->argn) {
1429       l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
1430       if (arg->argc > arg->argn+1)
1431         l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
1432     }
1433     return 0;
1434   }
1435   return -1;
1436 }
1437 
1438 static int
1439 SetServer(struct cmdargs const *arg)
1440 {
1441   int res = -1;
1442 
1443   if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
1444     const char *port, *passwd, *mask;
1445     size_t mlen;
1446 
1447     /* What's what ? */
1448     port = arg->argv[arg->argn];
1449     if (arg->argc == arg->argn + 2) {
1450       passwd = arg->argv[arg->argn+1];
1451       mask = NULL;
1452     } else if (arg->argc == arg->argn + 3) {
1453       passwd = arg->argv[arg->argn+1];
1454       mask = arg->argv[arg->argn+2];
1455       mlen = strlen(mask);
1456       if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen ||
1457           (mlen == 4 && *mask != '0')) {
1458         log_Printf(LogWARN, "%s %s: %s: Invalid mask\n",
1459                    arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask);
1460         return -1;
1461       }
1462     } else if (arg->argc != arg->argn + 1)
1463       return -1;
1464     else if (strcasecmp(port, "none") == 0) {
1465       if (server_Clear(arg->bundle))
1466         log_Printf(LogPHASE, "Disabled server socket\n");
1467       return 0;
1468     } else if (strcasecmp(port, "open") == 0) {
1469       switch (server_Reopen(arg->bundle)) {
1470         case SERVER_OK:
1471           return 0;
1472         case SERVER_FAILED:
1473           log_Printf(LogWARN, "Failed to reopen server port\n");
1474           return 1;
1475         case SERVER_UNSET:
1476           log_Printf(LogWARN, "Cannot reopen unset server socket\n");
1477           return 1;
1478         default:
1479           break;
1480       }
1481       return -1;
1482     } else if (strcasecmp(port, "closed") == 0) {
1483       if (server_Close(arg->bundle))
1484         log_Printf(LogPHASE, "Closed server socket\n");
1485       else
1486         log_Printf(LogWARN, "Server socket not open\n");
1487 
1488       return 0;
1489     } else
1490       return -1;
1491 
1492     strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1);
1493     server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0';
1494 
1495     if (*port == '/') {
1496       mode_t imask;
1497       char *ptr, name[LINE_LEN + 12];
1498 
1499       if (mask == NULL)
1500         imask = (mode_t)-1;
1501       else for (imask = mlen = 0; mask[mlen]; mlen++)
1502         imask = (imask * 8) + mask[mlen] - '0';
1503 
1504       ptr = strstr(port, "%d");
1505       if (ptr) {
1506         snprintf(name, sizeof name, "%.*s%d%s",
1507                  (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
1508         port = name;
1509       }
1510       res = server_LocalOpen(arg->bundle, port, imask);
1511     } else {
1512       int iport, add = 0;
1513 
1514       if (mask != NULL)
1515         return -1;
1516 
1517       if (*port == '+') {
1518         port++;
1519         add = 1;
1520       }
1521       if (strspn(port, "0123456789") != strlen(port)) {
1522         struct servent *s;
1523 
1524         if ((s = getservbyname(port, "tcp")) == NULL) {
1525 	  iport = 0;
1526 	  log_Printf(LogWARN, "%s: Invalid port or service\n", port);
1527 	} else
1528 	  iport = ntohs(s->s_port);
1529       } else
1530         iport = atoi(port);
1531 
1532       if (iport) {
1533         if (add)
1534           iport += arg->bundle->unit;
1535         res = server_TcpOpen(arg->bundle, iport);
1536       } else
1537         res = -1;
1538     }
1539   }
1540 
1541   return res;
1542 }
1543 
1544 static int
1545 SetEscape(struct cmdargs const *arg)
1546 {
1547   int code;
1548   int argc = arg->argc - arg->argn;
1549   char const *const *argv = arg->argv + arg->argn;
1550 
1551   for (code = 0; code < 33; code++)
1552     arg->cx->physical->async.cfg.EscMap[code] = 0;
1553 
1554   while (argc-- > 0) {
1555     sscanf(*argv++, "%x", &code);
1556     code &= 0xff;
1557     arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
1558     arg->cx->physical->async.cfg.EscMap[32] = 1;
1559   }
1560   return 0;
1561 }
1562 
1563 static int
1564 SetInterfaceAddr(struct cmdargs const *arg)
1565 {
1566   struct ncp *ncp = &arg->bundle->ncp;
1567   struct ncpaddr ncpaddr;
1568   const char *hisaddr;
1569 
1570   if (arg->argc > arg->argn + 4)
1571     return -1;
1572 
1573   hisaddr = NULL;
1574   memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range);
1575   memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range);
1576   ncp->ipcp.cfg.HaveTriggerAddress = 0;
1577   ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY;
1578   iplist_reset(&ncp->ipcp.cfg.peer_list);
1579 
1580   if (arg->argc > arg->argn) {
1581     if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn]))
1582       return 1;
1583     if (arg->argc > arg->argn+1) {
1584       hisaddr = arg->argv[arg->argn+1];
1585       if (arg->argc > arg->argn+2) {
1586         ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask =
1587           GetIpAddr(arg->argv[arg->argn+2]);
1588 	if (arg->argc > arg->argn+3) {
1589 	  ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
1590 	  ncp->ipcp.cfg.HaveTriggerAddress = 1;
1591 	}
1592       }
1593     }
1594   }
1595 
1596   /* 0.0.0.0 means any address (0 bits) */
1597   ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
1598   ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
1599   if (ncp->ipcp.my_ip.s_addr == INADDR_ANY)
1600     ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0);
1601   bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL);
1602 
1603   if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
1604                                   arg->bundle->phys_type.all & PHYS_AUTO))
1605     return 4;
1606 
1607   return 0;
1608 }
1609 
1610 static int
1611 SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
1612           u_int *maxtrm, int def)
1613 {
1614   if (argc == 0) {
1615     *timeout = DEF_FSMRETRY;
1616     *maxreq = def;
1617     if (maxtrm != NULL)
1618       *maxtrm = def;
1619   } else {
1620     long l = atol(argv[0]);
1621 
1622     if (l < MIN_FSMRETRY) {
1623       log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
1624                  l, MIN_FSMRETRY);
1625       return 1;
1626     } else
1627       *timeout = l;
1628 
1629     if (argc > 1) {
1630       l = atol(argv[1]);
1631       if (l < 1) {
1632         log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
1633         l = 1;
1634       }
1635       *maxreq = l;
1636 
1637       if (argc > 2 && maxtrm != NULL) {
1638         l = atol(argv[2]);
1639         if (l < 1) {
1640           log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
1641           l = 1;
1642         }
1643         *maxtrm = l;
1644       }
1645     }
1646   }
1647 
1648   return 0;
1649 }
1650 
1651 static int
1652 SetVariable(struct cmdargs const *arg)
1653 {
1654   long long_val, param = (long)arg->cmd->args;
1655   int mode, dummyint, f, first, res;
1656   u_short *change;
1657   const char *argp;
1658   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
1659   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
1660   struct in_addr *ipaddr;
1661   struct ncpaddr ncpaddr[2];
1662 
1663   if (arg->argc > arg->argn)
1664     argp = arg->argv[arg->argn];
1665   else
1666     argp = "";
1667 
1668   res = 0;
1669 
1670   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
1671     log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
1672               arg->cmd->name);
1673     return 1;
1674   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
1675     log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
1676               arg->cmd->name, cx->name);
1677     cx = NULL;
1678   }
1679 
1680   switch (param) {
1681   case VAR_AUTHKEY:
1682     strncpy(arg->bundle->cfg.auth.key, argp,
1683             sizeof arg->bundle->cfg.auth.key - 1);
1684     arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
1685     break;
1686 
1687   case VAR_AUTHNAME:
1688     switch (bundle_Phase(arg->bundle)) {
1689       default:
1690         log_Printf(LogWARN, "Altering authname while at phase %s\n",
1691                    bundle_PhaseName(arg->bundle));
1692         /* drop through */
1693       case PHASE_DEAD:
1694       case PHASE_ESTABLISH:
1695         strncpy(arg->bundle->cfg.auth.name, argp,
1696                 sizeof arg->bundle->cfg.auth.name - 1);
1697         arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
1698         break;
1699     }
1700     break;
1701 
1702   case VAR_AUTOLOAD:
1703     if (arg->argc == arg->argn + 3) {
1704       int v1, v2, v3;
1705       char *end;
1706 
1707       v1 = strtol(arg->argv[arg->argn], &end, 0);
1708       if (v1 < 0 || *end) {
1709         log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n",
1710                    arg->argv[arg->argn]);
1711         res = 1;
1712         break;
1713       }
1714 
1715       v2 = strtol(arg->argv[arg->argn + 1], &end, 0);
1716       if (v2 < 0 || *end) {
1717         log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n",
1718                    arg->argv[arg->argn + 1]);
1719         res = 1;
1720         break;
1721       }
1722       if (v2 < v1) {
1723         v3 = v1;
1724         v1 = v2;
1725         v2 = v3;
1726       }
1727 
1728       v3 = strtol(arg->argv[arg->argn + 2], &end, 0);
1729       if (v3 <= 0 || *end) {
1730         log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n",
1731                    arg->argv[arg->argn + 2]);
1732         res = 1;
1733         break;
1734       }
1735 
1736       arg->bundle->ncp.mp.cfg.autoload.min = v1;
1737       arg->bundle->ncp.mp.cfg.autoload.max = v2;
1738       arg->bundle->ncp.mp.cfg.autoload.period = v3;
1739       mp_RestartAutoloadTimer(&arg->bundle->ncp.mp);
1740     } else {
1741       log_Printf(LogWARN, "Set autoload requires three arguments\n");
1742       res = 1;
1743     }
1744     break;
1745 
1746   case VAR_DIAL:
1747     strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
1748     cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
1749     break;
1750 
1751   case VAR_LOGIN:
1752     strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
1753     cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
1754     break;
1755 
1756   case VAR_WINSIZE:
1757     if (arg->argc > arg->argn) {
1758       l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
1759       if (l->ccp.cfg.deflate.out.winsize < 8 ||
1760           l->ccp.cfg.deflate.out.winsize > 15) {
1761           log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
1762                     l->ccp.cfg.deflate.out.winsize);
1763           l->ccp.cfg.deflate.out.winsize = 15;
1764       }
1765       if (arg->argc > arg->argn+1) {
1766         l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
1767         if (l->ccp.cfg.deflate.in.winsize < 8 ||
1768             l->ccp.cfg.deflate.in.winsize > 15) {
1769             log_Printf(LogWARN, "%d: Invalid incoming window size\n",
1770                       l->ccp.cfg.deflate.in.winsize);
1771             l->ccp.cfg.deflate.in.winsize = 15;
1772         }
1773       } else
1774         l->ccp.cfg.deflate.in.winsize = 0;
1775     } else {
1776       log_Printf(LogWARN, "No window size specified\n");
1777       res = 1;
1778     }
1779     break;
1780 
1781 #ifndef NODES
1782   case VAR_MPPE:
1783     if (arg->argc > arg->argn + 2) {
1784       res = -1;
1785       break;
1786     }
1787 
1788     if (arg->argc == arg->argn) {
1789       l->ccp.cfg.mppe.keybits = 0;
1790       l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1791       l->ccp.cfg.mppe.required = 0;
1792       break;
1793     }
1794 
1795     if (!strcmp(argp, "*"))
1796       long_val = 0;
1797     else {
1798       long_val = atol(argp);
1799       if (long_val != 40 && long_val != 56 && long_val != 128) {
1800         log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
1801         res = -1;
1802         break;
1803       }
1804     }
1805 
1806     if (arg->argc == arg->argn + 2) {
1807       if (!strcmp(arg->argv[arg->argn + 1], "*"))
1808         l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1809       else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
1810         l->ccp.cfg.mppe.state = MPPE_STATELESS;
1811       else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful"))
1812         l->ccp.cfg.mppe.state = MPPE_STATEFUL;
1813       else {
1814         log_Printf(LogWARN, "%s: Invalid state value\n",
1815                    arg->argv[arg->argn + 1]);
1816         res = -1;
1817         break;
1818       }
1819     } else
1820       l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
1821     l->ccp.cfg.mppe.keybits = long_val;
1822     l->ccp.cfg.mppe.required = 1;
1823     break;
1824 #endif
1825 
1826   case VAR_DEVICE:
1827     physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
1828                            arg->argv + arg->argn);
1829     break;
1830 
1831   case VAR_ACCMAP:
1832     if (arg->argc > arg->argn) {
1833       u_long ulong_val;
1834       sscanf(argp, "%lx", &ulong_val);
1835       cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
1836     } else {
1837       log_Printf(LogWARN, "No accmap specified\n");
1838       res = 1;
1839     }
1840     break;
1841 
1842   case VAR_MODE:
1843     mode = Nam2mode(argp);
1844     if (mode == PHYS_NONE || mode == PHYS_ALL) {
1845       log_Printf(LogWARN, "%s: Invalid mode\n", argp);
1846       res = -1;
1847       break;
1848     }
1849     bundle_SetMode(arg->bundle, cx, mode);
1850     break;
1851 
1852   case VAR_MRRU:
1853     switch (bundle_Phase(arg->bundle)) {
1854       case PHASE_DEAD:
1855         break;
1856       case PHASE_ESTABLISH:
1857         /* Make sure none of our links are DATALINK_LCP or greater */
1858         if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
1859           log_Printf(LogWARN, "mrru: Only changeable before LCP negotiations\n");
1860           res = 1;
1861           break;
1862         }
1863         break;
1864       default:
1865         log_Printf(LogWARN, "mrru: Only changeable at phase DEAD/ESTABLISH\n");
1866         res = 1;
1867         break;
1868     }
1869     if (res != 0)
1870       break;
1871     long_val = atol(argp);
1872     if (long_val && long_val < MIN_MRU) {
1873       log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
1874       res = 1;
1875       break;
1876     } else if (long_val > MAX_MRU) {
1877       log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
1878       res = 1;
1879       break;
1880     } else
1881       arg->bundle->ncp.mp.cfg.mrru = long_val;
1882     break;
1883 
1884   case VAR_MRU:
1885     long_val = 0;	/* silence gcc */
1886     change = NULL;	/* silence gcc */
1887     switch(arg->argc - arg->argn) {
1888     case 1:
1889       if (argp[strspn(argp, "0123456789")] != '\0') {
1890         res = -1;
1891         break;
1892       }
1893       /*FALLTHRU*/
1894     case 0:
1895       long_val = atol(argp);
1896       change = &l->lcp.cfg.mru;
1897       if (long_val > l->lcp.cfg.max_mru) {
1898         log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val,
1899                    l->lcp.cfg.max_mru);
1900         res = 1;
1901         break;
1902       }
1903       break;
1904     case 2:
1905       if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1906         res = -1;
1907         break;
1908       }
1909       long_val = atol(arg->argv[arg->argn + 1]);
1910       change = &l->lcp.cfg.max_mru;
1911       if (long_val > MAX_MRU) {
1912         log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val,
1913                    MAX_MRU);
1914         res = 1;
1915         break;
1916       }
1917       break;
1918     default:
1919       res = -1;
1920       break;
1921     }
1922     if (res != 0)
1923       break;
1924 
1925     if (long_val == 0)
1926       *change = 0;
1927     else if (long_val < MIN_MRU) {
1928       log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
1929       res = 1;
1930       break;
1931     } else if (long_val > MAX_MRU) {
1932       log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
1933       res = 1;
1934       break;
1935     } else
1936       *change = long_val;
1937     if (l->lcp.cfg.mru > *change)
1938       l->lcp.cfg.mru = *change;
1939     break;
1940 
1941   case VAR_MTU:
1942     long_val = 0;	/* silence gcc */
1943     change = NULL;	/* silence gcc */
1944     switch(arg->argc - arg->argn) {
1945     case 1:
1946       if (argp[strspn(argp, "0123456789")] != '\0') {
1947         res = -1;
1948         break;
1949       }
1950       /*FALLTHRU*/
1951     case 0:
1952       long_val = atol(argp);
1953       change = &l->lcp.cfg.mtu;
1954       if (long_val > l->lcp.cfg.max_mtu) {
1955         log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val,
1956                    l->lcp.cfg.max_mtu);
1957         res = 1;
1958         break;
1959       }
1960       break;
1961     case 2:
1962       if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
1963         res = -1;
1964         break;
1965       }
1966       long_val = atol(arg->argv[arg->argn + 1]);
1967       change = &l->lcp.cfg.max_mtu;
1968       if (long_val > MAX_MTU) {
1969         log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val,
1970                    MAX_MTU);
1971         res = 1;
1972         break;
1973       }
1974       break;
1975     default:
1976       res = -1;
1977       break;
1978     }
1979 
1980     if (res != 0)
1981       break;
1982 
1983     if (long_val && long_val < MIN_MTU) {
1984       log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
1985       res = 1;
1986       break;
1987     } else if (long_val > MAX_MTU) {
1988       log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
1989       res = 1;
1990       break;
1991     } else
1992       *change = long_val;
1993     if (l->lcp.cfg.mtu > *change)
1994       l->lcp.cfg.mtu = *change;
1995     break;
1996 
1997   case VAR_OPENMODE:
1998     if (strcasecmp(argp, "active") == 0)
1999       cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
2000         atoi(arg->argv[arg->argn+1]) : 1;
2001     else if (strcasecmp(argp, "passive") == 0)
2002       cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
2003     else {
2004       log_Printf(LogWARN, "%s: Invalid openmode\n", argp);
2005       res = 1;
2006     }
2007     break;
2008 
2009   case VAR_PHONE:
2010     strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
2011     cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
2012     cx->phone.alt = cx->phone.next = NULL;
2013     break;
2014 
2015   case VAR_HANGUP:
2016     strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
2017     cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
2018     break;
2019 
2020   case VAR_IFQUEUE:
2021     long_val = atol(argp);
2022     arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val;
2023     break;
2024 
2025   case VAR_LOGOUT:
2026     strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1);
2027     cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0';
2028     break;
2029 
2030   case VAR_IDLETIMEOUT:
2031     if (arg->argc > arg->argn+2) {
2032       log_Printf(LogWARN, "Too many idle timeout values\n");
2033       res = 1;
2034     } else if (arg->argc == arg->argn) {
2035       log_Printf(LogWARN, "Too few idle timeout values\n");
2036       res = 1;
2037     } else {
2038       unsigned long timeout, min;
2039 
2040       timeout = strtoul(argp, NULL, 10);
2041       min = arg->bundle->cfg.idle.min_timeout;
2042       if (arg->argc == arg->argn + 2)
2043 	min = strtoul(arg->argv[arg->argn + 1], NULL, 10);
2044       bundle_SetIdleTimer(arg->bundle, timeout, min);
2045     }
2046     break;
2047 
2048 #ifndef NORADIUS
2049   case VAR_RAD_ALIVE:
2050     if (arg->argc > arg->argn + 2) {
2051       log_Printf(LogWARN, "Too many RADIUS alive interval values\n");
2052       res = 1;
2053     } else if (arg->argc == arg->argn) {
2054       log_Printf(LogWARN, "Too few RADIUS alive interval values\n");
2055       res = 1;
2056     } else {
2057       arg->bundle->radius.alive.interval = atoi(argp);
2058       if (arg->bundle->radius.alive.interval && !*arg->bundle->radius.cfg.file) {
2059         log_Printf(LogWARN, "rad_alive requires radius to be configured\n");
2060 	res = 1;
2061       } else if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) {
2062 	if (arg->bundle->radius.alive.interval)
2063 	  radius_StartTimer(arg->bundle);
2064 	else
2065 	  radius_StopTimer(&arg->bundle->radius);
2066       }
2067     }
2068     break;
2069 #endif
2070 
2071   case VAR_LQRPERIOD:
2072     long_val = atol(argp);
2073     if (long_val < MIN_LQRPERIOD) {
2074       log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
2075                  long_val, MIN_LQRPERIOD);
2076       res = 1;
2077     } else
2078       l->lcp.cfg.lqrperiod = long_val;
2079     break;
2080 
2081   case VAR_LCPRETRY:
2082     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2083                    &cx->physical->link.lcp.cfg.fsm.timeout,
2084                    &cx->physical->link.lcp.cfg.fsm.maxreq,
2085                    &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2086     break;
2087 
2088   case VAR_CHAPRETRY:
2089     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2090                    &cx->chap.auth.cfg.fsm.timeout,
2091                    &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
2092     break;
2093 
2094   case VAR_PAPRETRY:
2095     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2096                    &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
2097                    NULL, DEF_FSMAUTHTRIES);
2098     break;
2099 
2100   case VAR_CCPRETRY:
2101     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2102                    &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
2103                    &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2104     break;
2105 
2106   case VAR_IPCPRETRY:
2107     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2108                    &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
2109                    &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
2110                    &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2111     break;
2112 
2113 #ifndef NOINET6
2114   case VAR_IPV6CPRETRY:
2115     res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
2116                    &arg->bundle->ncp.ipv6cp.cfg.fsm.timeout,
2117                    &arg->bundle->ncp.ipv6cp.cfg.fsm.maxreq,
2118                    &arg->bundle->ncp.ipv6cp.cfg.fsm.maxtrm, DEF_FSMTRIES);
2119     break;
2120 #endif
2121 
2122   case VAR_NBNS:
2123   case VAR_DNS:
2124     if (param == VAR_DNS) {
2125       ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns;
2126       ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE;
2127     } else {
2128       ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
2129       ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY;
2130     }
2131 
2132     if (arg->argc > arg->argn) {
2133       ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]);
2134       if (!ncpaddr_getip4(ncpaddr, ipaddr))
2135         return -1;
2136       if (arg->argc > arg->argn+1) {
2137         ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]);
2138         if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1))
2139           return -1;
2140       }
2141 
2142       if (ipaddr[0].s_addr == INADDR_ANY) {
2143         ipaddr[0] = ipaddr[1];
2144         ipaddr[1].s_addr = INADDR_ANY;
2145       }
2146       if (ipaddr[0].s_addr == INADDR_NONE) {
2147         ipaddr[0] = ipaddr[1];
2148         ipaddr[1].s_addr = INADDR_NONE;
2149       }
2150     }
2151     break;
2152 
2153   case VAR_CALLBACK:
2154     cx->cfg.callback.opmask = 0;
2155     for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
2156       if (!strcasecmp(arg->argv[dummyint], "auth"))
2157         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
2158       else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
2159         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
2160       else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
2161         if (dummyint == arg->argc - 1)
2162           log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
2163         else {
2164           cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
2165           strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
2166                   sizeof cx->cfg.callback.msg - 1);
2167           cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
2168         }
2169       } else if (!strcasecmp(arg->argv[dummyint], "none"))
2170         cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
2171       else {
2172         res = -1;
2173         break;
2174       }
2175     }
2176     if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
2177       cx->cfg.callback.opmask = 0;
2178     break;
2179 
2180   case VAR_CBCP:
2181     cx->cfg.cbcp.delay = 0;
2182     *cx->cfg.cbcp.phone = '\0';
2183     cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
2184     if (arg->argc > arg->argn) {
2185       strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
2186               sizeof cx->cfg.cbcp.phone - 1);
2187       cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
2188       if (arg->argc > arg->argn + 1) {
2189         cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
2190         if (arg->argc > arg->argn + 2) {
2191           long_val = atol(arg->argv[arg->argn + 2]);
2192           if (long_val < MIN_FSMRETRY)
2193             log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
2194                        long_val, MIN_FSMRETRY);
2195           else
2196             cx->cfg.cbcp.fsmretry = long_val;
2197         }
2198       }
2199     }
2200     break;
2201 
2202   case VAR_CHOKED:
2203     arg->bundle->cfg.choked.timeout = atoi(argp);
2204     if (arg->bundle->cfg.choked.timeout <= 0)
2205       arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
2206     break;
2207 
2208   case VAR_SENDPIPE:
2209     long_val = atol(argp);
2210     arg->bundle->ncp.cfg.sendpipe = long_val;
2211     break;
2212 
2213   case VAR_RECVPIPE:
2214     long_val = atol(argp);
2215     arg->bundle->ncp.cfg.recvpipe = long_val;
2216     break;
2217 
2218 #ifndef NORADIUS
2219   case VAR_RADIUS:
2220     if (!*argp)
2221       *arg->bundle->radius.cfg.file = '\0';
2222     else if (access(argp, R_OK)) {
2223       log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
2224       res = 1;
2225       break;
2226     } else {
2227       strncpy(arg->bundle->radius.cfg.file, argp,
2228               sizeof arg->bundle->radius.cfg.file - 1);
2229       arg->bundle->radius.cfg.file
2230         [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
2231     }
2232     break;
2233 #endif
2234 
2235   case VAR_CD:
2236     if (*argp) {
2237       if (strcasecmp(argp, "off")) {
2238         long_val = atol(argp);
2239         if (long_val < 0)
2240           long_val = 0;
2241         cx->physical->cfg.cd.delay = long_val;
2242         cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ?
2243           CD_REQUIRED : CD_VARIABLE;
2244       } else
2245         cx->physical->cfg.cd.necessity = CD_NOTREQUIRED;
2246     } else {
2247       cx->physical->cfg.cd.delay = 0;
2248       cx->physical->cfg.cd.necessity = CD_DEFAULT;
2249     }
2250     break;
2251 
2252   case VAR_PARITY:
2253     if (arg->argc == arg->argn + 1)
2254       res = physical_SetParity(arg->cx->physical, argp);
2255     else {
2256       log_Printf(LogWARN, "Parity value must be odd, even or none\n");
2257       res = 1;
2258     }
2259     break;
2260 
2261   case VAR_CRTSCTS:
2262     if (strcasecmp(argp, "on") == 0)
2263       physical_SetRtsCts(arg->cx->physical, 1);
2264     else if (strcasecmp(argp, "off") == 0)
2265       physical_SetRtsCts(arg->cx->physical, 0);
2266     else {
2267       log_Printf(LogWARN, "RTS/CTS value must be on or off\n");
2268       res = 1;
2269     }
2270     break;
2271 
2272   case VAR_URGENT:
2273     if (arg->argn == arg->argc) {
2274       ncp_SetUrgentTOS(&arg->bundle->ncp);
2275       ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2276       ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2277     } else if (!strcasecmp(arg->argv[arg->argn], "udp")) {
2278       ncp_SetUrgentTOS(&arg->bundle->ncp);
2279       if (arg->argn == arg->argc - 1)
2280         ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2281       else for (f = arg->argn + 1; f < arg->argc; f++)
2282         if (*arg->argv[f] == '+')
2283           ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2284         else if (*arg->argv[f] == '-')
2285           ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2286         else {
2287           if (f == arg->argn)
2288             ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2289           ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2290         }
2291     } else if (arg->argn == arg->argc - 1 &&
2292                !strcasecmp(arg->argv[arg->argn], "none")) {
2293       ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2294       ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
2295       ncp_ClearUrgentTOS(&arg->bundle->ncp);
2296     } else if (!strcasecmp(arg->argv[arg->argn], "length")) {
2297       if (arg->argn == arg->argc - 1)
2298 	ncp_SetUrgentTcpLen(&arg->bundle->ncp, 0);
2299       else
2300 	ncp_SetUrgentTcpLen(&arg->bundle->ncp, atoi(arg->argv[arg->argn + 1]));
2301     } else {
2302       ncp_SetUrgentTOS(&arg->bundle->ncp);
2303       first = arg->argn;
2304       if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc)
2305         ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2306 
2307       for (f = first; f < arg->argc; f++)
2308         if (*arg->argv[f] == '+')
2309           ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2310         else if (*arg->argv[f] == '-')
2311           ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
2312         else {
2313           if (f == first)
2314             ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
2315           ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
2316         }
2317     }
2318     break;
2319 
2320   case VAR_PPPOE:
2321     if (strcasecmp(argp, "3Com") == 0)
2322       physical_SetPPPoEnonstandard(arg->cx->physical, 1);
2323     else if (strcasecmp(argp, "standard") == 0)
2324       physical_SetPPPoEnonstandard(arg->cx->physical, 0);
2325     else {
2326       log_Printf(LogWARN, "PPPoE standard value must be \"standard\" or \"3Com\"\n");
2327       res = 1;
2328     }
2329     break;
2330 
2331 #ifndef NORADIUS
2332   case VAR_PORT_ID:
2333     if (strcasecmp(argp, "default") == 0)
2334 	    arg->bundle->radius.port_id_type = RPI_DEFAULT;
2335     else if (strcasecmp(argp, "pid") == 0)
2336 	    arg->bundle->radius.port_id_type = RPI_PID;
2337     else if (strcasecmp(argp, "ifnum") == 0)
2338 	    arg->bundle->radius.port_id_type = RPI_IFNUM;
2339     else if (strcasecmp(argp, "tunnum") == 0)
2340 	    arg->bundle->radius.port_id_type = RPI_TUNNUM;
2341     else {
2342 	   log_Printf(LogWARN,
2343 		"RADIUS port id must be one of \"default\", \"pid\", \"ifnum\" or \"tunnum\"\n");
2344 	   res = 1;
2345     }
2346 
2347     if (arg->bundle->radius.port_id_type && !*arg->bundle->radius.cfg.file) {
2348 	    log_Printf(LogWARN, "rad_port_id requires radius to be configured\n");
2349 	    res = 1;
2350     }
2351 
2352     break;
2353 #endif
2354   }
2355 
2356   return res;
2357 }
2358 
2359 static struct cmdtab const SetCommands[] = {
2360   {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2361   "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
2362   {"authkey", "key", SetVariable, LOCAL_AUTH,
2363   "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
2364   {"authname", NULL, SetVariable, LOCAL_AUTH,
2365   "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
2366   {"autoload", NULL, SetVariable, LOCAL_AUTH,
2367   "auto link [de]activation", "set autoload maxtime maxload mintime minload",
2368   (const void *)VAR_AUTOLOAD},
2369   {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX,
2370   "datalink bandwidth", "set bandwidth value", NULL},
2371   {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2372   "callback control", "set callback [none|auth|cbcp|"
2373   "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
2374   {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2375   "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
2376   (const void *)VAR_CBCP},
2377   {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2378    "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
2379   {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
2380    "set cd value[!]", (const void *)VAR_CD},
2381   {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
2382    "CHAP retries", "set chapretry value [attempts]",
2383    (const void *)VAR_CHAPRETRY},
2384   {"choked", NULL, SetVariable, LOCAL_AUTH,
2385   "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
2386   {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
2387    "Use hardware flow control", "set ctsrts [on|off]",
2388    (const char *)VAR_CRTSCTS},
2389   {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2390   "deflate window sizes", "set deflate out-winsize in-winsize",
2391   (const void *) VAR_WINSIZE},
2392 #ifndef NODES
2393   {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2394   "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]",
2395   (const void *) VAR_MPPE},
2396 #endif
2397   {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
2398   "physical device name", "set device|line device-name[,device-name]",
2399   (const void *) VAR_DEVICE},
2400   {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2401   "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
2402   {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
2403   "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
2404   {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
2405   "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]", NULL},
2406   {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
2407   "escape characters", "set escape hex-digit ...", NULL},
2408   {"filter", NULL, filter_Set, LOCAL_AUTH,
2409   "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
2410   "[src_addr[/width]] [dst_addr[/width]] [proto "
2411   "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]", NULL},
2412   {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2413   "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
2414   {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
2415   "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]", NULL},
2416   {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue",
2417   "set ifqueue packets", (const void *)VAR_IFQUEUE},
2418   {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
2419    "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
2420   {"ipv6cpretry", "ipv6cpretries", SetVariable, LOCAL_AUTH, "IPV6CP retries",
2421    "set ipv6cpretry value [attempts]", (const void *)VAR_IPV6CPRETRY},
2422   {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
2423    "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
2424   {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
2425   "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|"
2426   "id0|ipcp|lcp|lqm|phase|physical|radius|sync|tcp/ip|timer|tun...", NULL},
2427   {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2428   "login script", "set login chat-script", (const void *) VAR_LOGIN},
2429   {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2430   "logout script", "set logout chat-script", (const void *) VAR_LOGOUT},
2431   {"lqrperiod", "echoperiod", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
2432   "LQR period", "set lqr/echo period value", (const void *)VAR_LQRPERIOD},
2433   {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
2434   "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
2435   {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
2436   "set mrru value", (const void *)VAR_MRRU},
2437   {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2438   "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU},
2439   {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2440   "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU},
2441   {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
2442   "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
2443   {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
2444   "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
2445   {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
2446    "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
2447   {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
2448    "set parity [odd|even|none]", (const void *)VAR_PARITY},
2449   {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
2450   "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
2451   {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
2452   "Process title", "set proctitle [value]", NULL},
2453 #ifndef NORADIUS
2454   {"radius", NULL, SetVariable, LOCAL_AUTH,
2455   "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
2456   {"rad_alive", NULL, SetVariable, LOCAL_AUTH,
2457   "Raduis alive interval", "set rad_alive value",
2458   (const void *)VAR_RAD_ALIVE},
2459   {"rad_port_id", NULL, SetVariable, LOCAL_AUTH,
2460   "NAS-Port-Id", "set rad_port_id [default|pid|ifnum|tunnum]", (const void *)VAR_PORT_ID},
2461 #endif
2462   {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
2463   "Reconnect timeout", "set reconnect value ntries", NULL},
2464   {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
2465   "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
2466   {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
2467   "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]", NULL},
2468   {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
2469   "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
2470   {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port",
2471   "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]",
2472   NULL},
2473   {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
2474   "physical speed", "set speed value|sync", NULL},
2475   {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
2476   "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]", NULL},
2477   {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
2478   "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
2479   {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent traffic",
2480   "set urgent [[tcp|udp] [+|-]port...]|[length len]", (const void *)VAR_URGENT},
2481   {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
2482   "vj values", "set vj slots|slotcomp [value]", NULL},
2483   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
2484   "Display this message", "set help|? [command]", SetCommands},
2485   {"pppoe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
2486    "Connect using standard/3Com mode", "set pppoe [standard|3Com]",
2487    (const char *)VAR_PPPOE},
2488   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
2489 };
2490 
2491 static int
2492 SetCommand(struct cmdargs const *arg)
2493 {
2494   if (arg->argc > arg->argn)
2495     FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
2496              arg->prompt, arg->cx);
2497   else if (arg->prompt)
2498     prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
2499 	          " syntax help.\n");
2500   else
2501     log_Printf(LogWARN, "set command must have arguments\n");
2502 
2503   return 0;
2504 }
2505 
2506 static int
2507 AddCommand(struct cmdargs const *arg)
2508 {
2509   struct ncpaddr gw;
2510   struct ncprange dest;
2511   struct in_addr host;
2512 #ifndef NOINET6
2513   struct in6_addr host6;
2514 #endif
2515   int dest_default, gw_arg, addrs;
2516 
2517   if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
2518     return -1;
2519 
2520   addrs = 0;
2521   dest_default = 0;
2522   if (arg->argc == arg->argn + 2) {
2523     if (!strcasecmp(arg->argv[arg->argn], "default"))
2524       dest_default = 1;
2525     else {
2526       if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]))
2527         return -1;
2528       if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
2529         addrs = ROUTE_DSTMYADDR;
2530       else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7))
2531         addrs = ROUTE_DSTMYADDR6;
2532       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
2533         addrs = ROUTE_DSTHISADDR;
2534       else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8))
2535         addrs = ROUTE_DSTHISADDR6;
2536       else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4))
2537         addrs = ROUTE_DSTDNS0;
2538       else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4))
2539         addrs = ROUTE_DSTDNS1;
2540     }
2541     gw_arg = 1;
2542   } else {
2543     if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2544       addrs = ROUTE_DSTMYADDR;
2545       host = arg->bundle->ncp.ipcp.my_ip;
2546     } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2547       addrs = ROUTE_DSTHISADDR;
2548       host = arg->bundle->ncp.ipcp.peer_ip;
2549     } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2550       addrs = ROUTE_DSTDNS0;
2551       host = arg->bundle->ncp.ipcp.ns.dns[0];
2552     } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2553       addrs = ROUTE_DSTDNS1;
2554       host = arg->bundle->ncp.ipcp.ns.dns[1];
2555     } else {
2556       host = GetIpAddr(arg->argv[arg->argn]);
2557       if (host.s_addr == INADDR_NONE) {
2558         log_Printf(LogWARN, "%s: Invalid destination address\n",
2559                    arg->argv[arg->argn]);
2560         return -1;
2561       }
2562     }
2563     ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1]));
2564     gw_arg = 2;
2565   }
2566 
2567   if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) {
2568     ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip);
2569     addrs |= ROUTE_GWHISADDR;
2570 #ifndef NOINET6
2571   } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) {
2572     if (!ncpaddr_getip6(&arg->bundle->ncp.ipv6cp.hisaddr, &host6))
2573       memset(&host6, '\0', sizeof host6);
2574     ncpaddr_setip6(&gw, &host6);
2575     addrs |= ROUTE_GWHISADDR6;
2576 #endif
2577   } else {
2578     if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) {
2579       log_Printf(LogWARN, "%s: Invalid gateway address\n",
2580                  arg->argv[arg->argn + gw_arg]);
2581       return -1;
2582     }
2583   }
2584 
2585   if (dest_default)
2586     ncprange_setdefault(&dest, ncpaddr_family(&gw));
2587 
2588   if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0,
2589              ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0)
2590       && addrs != ROUTE_STATIC)
2591     route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw);
2592 
2593   return 0;
2594 }
2595 
2596 static int
2597 DeleteCommand(struct cmdargs const *arg)
2598 {
2599   struct ncprange dest;
2600   int addrs;
2601 
2602   if (arg->argc == arg->argn+1) {
2603     if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
2604       route_IfDelete(arg->bundle, 0);
2605       route_DeleteAll(&arg->bundle->ncp.route);
2606     } else {
2607       addrs = 0;
2608       if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
2609         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip);
2610         addrs = ROUTE_DSTMYADDR;
2611 #ifndef NOINET6
2612       } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) {
2613         ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr);
2614         addrs = ROUTE_DSTMYADDR6;
2615 #endif
2616       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
2617         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip);
2618         addrs = ROUTE_DSTHISADDR;
2619 #ifndef NOINET6
2620       } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) {
2621         ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr);
2622         addrs = ROUTE_DSTHISADDR6;
2623 #endif
2624       } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
2625         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]);
2626         addrs = ROUTE_DSTDNS0;
2627       } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
2628         ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]);
2629         addrs = ROUTE_DSTDNS1;
2630       } else {
2631         ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]);
2632         addrs = ROUTE_STATIC;
2633       }
2634       rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0);
2635       route_Delete(&arg->bundle->ncp.route, addrs, &dest);
2636     }
2637   } else
2638     return -1;
2639 
2640   return 0;
2641 }
2642 
2643 #ifndef NONAT
2644 static int
2645 NatEnable(struct cmdargs const *arg)
2646 {
2647   if (arg->argc == arg->argn+1) {
2648     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2649       if (!arg->bundle->NatEnabled) {
2650         if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
2651           LibAliasSetAddress(la, arg->bundle->ncp.ipcp.my_ip);
2652         arg->bundle->NatEnabled = 1;
2653       }
2654       return 0;
2655     } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
2656       arg->bundle->NatEnabled = 0;
2657       opt_disable(arg->bundle, OPT_IFACEALIAS);
2658       /* Don't iface_Clear() - there may be manually configured addresses */
2659       return 0;
2660     }
2661   }
2662 
2663   return -1;
2664 }
2665 
2666 
2667 static int
2668 NatOption(struct cmdargs const *arg)
2669 {
2670   long param = (long)arg->cmd->args;
2671 
2672   if (arg->argc == arg->argn+1) {
2673     if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
2674       if (arg->bundle->NatEnabled) {
2675 	LibAliasSetMode(la, param, param);
2676 	return 0;
2677       }
2678       log_Printf(LogWARN, "nat not enabled\n");
2679     } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
2680       if (arg->bundle->NatEnabled) {
2681 	LibAliasSetMode(la, 0, param);
2682 	return 0;
2683       }
2684       log_Printf(LogWARN, "nat not enabled\n");
2685     }
2686   }
2687   return -1;
2688 }
2689 #endif /* #ifndef NONAT */
2690 
2691 static int
2692 LinkCommand(struct cmdargs const *arg)
2693 {
2694   if (arg->argc > arg->argn+1) {
2695     char namelist[LINE_LEN];
2696     struct datalink *cx;
2697     char *name;
2698     int result = 0;
2699 
2700     if (!strcmp(arg->argv[arg->argn], "*")) {
2701       struct datalink *dl;
2702 
2703       cx = arg->bundle->links;
2704       while (cx) {
2705         /* Watch it, the command could be a ``remove'' */
2706         dl = cx->next;
2707         FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2708                  arg->prompt, cx);
2709         for (cx = arg->bundle->links; cx; cx = cx->next)
2710           if (cx == dl)
2711             break;		/* Pointer's still valid ! */
2712       }
2713     } else {
2714       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2715       namelist[sizeof namelist - 1] = '\0';
2716       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
2717         if (!bundle2datalink(arg->bundle, name)) {
2718           log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
2719           return 1;
2720         }
2721 
2722       strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
2723       namelist[sizeof namelist - 1] = '\0';
2724       for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
2725         cx = bundle2datalink(arg->bundle, name);
2726         if (cx)
2727           FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
2728                    arg->prompt, cx);
2729         else {
2730           log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
2731           result++;
2732         }
2733       }
2734     }
2735     return result;
2736   }
2737 
2738   log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax);
2739   return 2;
2740 }
2741 
2742 struct link *
2743 command_ChooseLink(struct cmdargs const *arg)
2744 {
2745   if (arg->cx)
2746     return &arg->cx->physical->link;
2747   else if (!arg->bundle->ncp.mp.cfg.mrru) {
2748     struct datalink *dl = bundle2datalink(arg->bundle, NULL);
2749     if (dl)
2750       return &dl->physical->link;
2751   }
2752   return &arg->bundle->ncp.mp.link;
2753 }
2754 
2755 static const char *
2756 ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
2757 {
2758   const char *result;
2759 
2760   switch (*cmd) {
2761     case 'A':
2762     case 'a':
2763       result = "accept";
2764       *keep = NEG_MYMASK;
2765       *add = NEG_ACCEPTED;
2766       break;
2767     case 'D':
2768     case 'd':
2769       switch (cmd[1]) {
2770         case 'E':
2771         case 'e':
2772           result = "deny";
2773           *keep = NEG_MYMASK;
2774           *add = 0;
2775           break;
2776         case 'I':
2777         case 'i':
2778           result = "disable";
2779           *keep = NEG_HISMASK;
2780           *add = 0;
2781           break;
2782         default:
2783           return NULL;
2784       }
2785       break;
2786     case 'E':
2787     case 'e':
2788       result = "enable";
2789       *keep = NEG_HISMASK;
2790       *add = NEG_ENABLED;
2791       break;
2792     default:
2793       return NULL;
2794   }
2795 
2796   return result;
2797 }
2798 
2799 static int
2800 OptSet(struct cmdargs const *arg)
2801 {
2802   int opt = (int)(long)arg->cmd->args;
2803   unsigned keep;			/* Keep this opt */
2804   unsigned add;				/* Add this opt */
2805 
2806   if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
2807     return 1;
2808 
2809 #ifndef NOINET6
2810   if (add == NEG_ENABLED && opt == OPT_IPV6CP && !probe.ipv6_available) {
2811     log_Printf(LogWARN, "IPv6 is not available on this machine\n");
2812     return 1;
2813   }
2814 #endif
2815   if (!add && ((opt == OPT_NAS_IP_ADDRESS &&
2816                 !Enabled(arg->bundle, OPT_NAS_IDENTIFIER)) ||
2817                (opt == OPT_NAS_IDENTIFIER &&
2818                 !Enabled(arg->bundle, OPT_NAS_IP_ADDRESS)))) {
2819     log_Printf(LogWARN,
2820                "Cannot disable both NAS-IP-Address and NAS-Identifier\n");
2821     return 1;
2822   }
2823 
2824   if (add)
2825     opt_enable(arg->bundle, opt);
2826   else
2827     opt_disable(arg->bundle, opt);
2828 
2829   return 0;
2830 }
2831 
2832 static int
2833 IfaceAliasOptSet(struct cmdargs const *arg)
2834 {
2835   unsigned long long save = arg->bundle->cfg.optmask;
2836   int result = OptSet(arg);
2837 
2838   if (result == 0)
2839     if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
2840       arg->bundle->cfg.optmask = save;
2841       log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n");
2842       result = 2;
2843     }
2844 
2845   return result;
2846 }
2847 
2848 static int
2849 NegotiateSet(struct cmdargs const *arg)
2850 {
2851   long param = (long)arg->cmd->args;
2852   struct link *l = command_ChooseLink(arg);	/* LOCAL_CX_OPT uses this */
2853   struct datalink *cx = arg->cx;	/* LOCAL_CX uses this */
2854   const char *cmd;
2855   unsigned keep;			/* Keep these bits */
2856   unsigned add;				/* Add these bits */
2857 
2858   if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
2859     return 1;
2860 
2861   if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
2862     log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
2863               cmd, arg->cmd->name);
2864     return 2;
2865   } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
2866     log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
2867               cmd, arg->cmd->name, cx->name);
2868     cx = NULL;
2869   }
2870 
2871   switch (param) {
2872     case NEG_ACFCOMP:
2873       cx->physical->link.lcp.cfg.acfcomp &= keep;
2874       cx->physical->link.lcp.cfg.acfcomp |= add;
2875       break;
2876     case NEG_CHAP05:
2877       cx->physical->link.lcp.cfg.chap05 &= keep;
2878       cx->physical->link.lcp.cfg.chap05 |= add;
2879       break;
2880 #ifndef NODES
2881     case NEG_CHAP80:
2882       cx->physical->link.lcp.cfg.chap80nt &= keep;
2883       cx->physical->link.lcp.cfg.chap80nt |= add;
2884       break;
2885     case NEG_CHAP80LM:
2886       cx->physical->link.lcp.cfg.chap80lm &= keep;
2887       cx->physical->link.lcp.cfg.chap80lm |= add;
2888       break;
2889     case NEG_CHAP81:
2890       cx->physical->link.lcp.cfg.chap81 &= keep;
2891       cx->physical->link.lcp.cfg.chap81 |= add;
2892       break;
2893     case NEG_MPPE:
2894       l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
2895       l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
2896       break;
2897 #endif
2898     case NEG_DEFLATE:
2899       l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
2900       l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
2901       break;
2902     case NEG_DNS:
2903       arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
2904       arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
2905       break;
2906     case NEG_ECHO:	/* probably misplaced in this function ! */
2907       if (cx->physical->link.lcp.cfg.echo && !add) {
2908         cx->physical->link.lcp.cfg.echo = 0;
2909         cx->physical->hdlc.lqm.method &= ~LQM_ECHO;
2910         if (cx->physical->hdlc.lqm.method & LQM_ECHO &&
2911             !cx->physical->link.lcp.want_lqrperiod &&
2912             cx->physical->hdlc.lqm.timer.load) {
2913           cx->physical->hdlc.lqm.timer.load = 0;
2914           lqr_StopTimer(cx->physical);
2915         }
2916       } else if (!cx->physical->link.lcp.cfg.echo && add) {
2917         cx->physical->link.lcp.cfg.echo = 1;
2918         cx->physical->hdlc.lqm.method |= LQM_ECHO;
2919         cx->physical->hdlc.lqm.timer.load =
2920 	    cx->physical->link.lcp.cfg.lqrperiod * SECTICKS;
2921         if (cx->physical->link.lcp.fsm.state == ST_OPENED)
2922           (*cx->physical->hdlc.lqm.timer.func)(&cx->physical->link.lcp);
2923       }
2924       break;
2925     case NEG_ENDDISC:
2926       arg->bundle->ncp.mp.cfg.negenddisc &= keep;
2927       arg->bundle->ncp.mp.cfg.negenddisc |= add;
2928       break;
2929     case NEG_LQR:
2930       cx->physical->link.lcp.cfg.lqr &= keep;
2931       cx->physical->link.lcp.cfg.lqr |= add;
2932       break;
2933     case NEG_PAP:
2934       cx->physical->link.lcp.cfg.pap &= keep;
2935       cx->physical->link.lcp.cfg.pap |= add;
2936       break;
2937     case NEG_PPPDDEFLATE:
2938       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
2939       l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
2940       break;
2941     case NEG_PRED1:
2942       l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
2943       l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
2944       break;
2945     case NEG_PROTOCOMP:
2946       cx->physical->link.lcp.cfg.protocomp &= keep;
2947       cx->physical->link.lcp.cfg.protocomp |= add;
2948       break;
2949     case NEG_SHORTSEQ:
2950       switch (bundle_Phase(arg->bundle)) {
2951         case PHASE_DEAD:
2952           break;
2953         case PHASE_ESTABLISH:
2954           /* Make sure none of our links are DATALINK_LCP or greater */
2955           if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
2956             log_Printf(LogWARN, "shortseq: Only changeable before"
2957                        " LCP negotiations\n");
2958             return 1;
2959           }
2960           break;
2961         default:
2962           log_Printf(LogWARN, "shortseq: Only changeable at phase"
2963                      " DEAD/ESTABLISH\n");
2964           return 1;
2965       }
2966       arg->bundle->ncp.mp.cfg.shortseq &= keep;
2967       arg->bundle->ncp.mp.cfg.shortseq |= add;
2968       break;
2969     case NEG_VJCOMP:
2970       arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
2971       arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
2972       break;
2973   }
2974 
2975   return 0;
2976 }
2977 
2978 static struct cmdtab const NegotiateCommands[] = {
2979   {"echo", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, "Send echo requests",
2980   "disable|enable", (const void *)NEG_ECHO},
2981   {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH,
2982   "filter on PPPoUDP payloads", "disable|enable",
2983   (const void *)OPT_FILTERDECAP},
2984   {"force-scripts", NULL, OptSet, LOCAL_AUTH,
2985    "Force execution of the configured chat scripts", "disable|enable",
2986    (const void *)OPT_FORCE_SCRIPTS},
2987   {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
2988   "disable|enable", (const void *)OPT_IDCHECK},
2989   {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
2990   "retain interface addresses", "disable|enable",
2991   (const void *)OPT_IFACEALIAS},
2992 #ifndef NOINET6
2993   {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol",
2994   "disable|enable", (const void *)OPT_IPCP},
2995   {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol",
2996   "disable|enable", (const void *)OPT_IPV6CP},
2997 #endif
2998   {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
2999   "disable|enable", (const void *)OPT_KEEPSESSION},
3000   {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
3001   "disable|enable", (const void *)OPT_LOOPBACK},
3002   {"nas-ip-address", NULL, OptSet, LOCAL_AUTH, "Send NAS-IP-Address to RADIUS",
3003   "disable|enable", (const void *)OPT_NAS_IP_ADDRESS},
3004   {"nas-identifier", NULL, OptSet, LOCAL_AUTH, "Send NAS-Identifier to RADIUS",
3005   "disable|enable", (const void *)OPT_NAS_IDENTIFIER},
3006   {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
3007   "disable|enable", (const void *)OPT_PASSWDAUTH},
3008   {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
3009   "disable|enable", (const void *)OPT_PROXY},
3010   {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
3011   "disable|enable", (const void *)OPT_PROXYALL},
3012   {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
3013   "disable|enable", (const void *)OPT_SROUTES},
3014   {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options",
3015   "disable|enable", (const void *)OPT_TCPMSSFIXUP},
3016   {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
3017   "disable|enable", (const void *)OPT_THROUGHPUT},
3018   {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
3019   "disable|enable", (const void *)OPT_UTMP},
3020 
3021 #ifndef NOINET6
3022 #define NEG_OPT_MAX 17	/* accept/deny allowed below and not above */
3023 #else
3024 #define NEG_OPT_MAX 15
3025 #endif
3026 
3027   {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3028   "Address & Control field compression", "accept|deny|disable|enable",
3029   (const void *)NEG_ACFCOMP},
3030   {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3031   "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
3032   (const void *)NEG_CHAP05},
3033 #ifndef NODES
3034   {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3035   "Microsoft (NT) CHAP", "accept|deny|disable|enable",
3036   (const void *)NEG_CHAP80},
3037   {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3038   "Microsoft (NT) CHAP", "accept|deny|disable|enable",
3039   (const void *)NEG_CHAP80LM},
3040   {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3041   "Microsoft CHAP v2", "accept|deny|disable|enable",
3042   (const void *)NEG_CHAP81},
3043   {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
3044   "MPPE encryption", "accept|deny|disable|enable",
3045   (const void *)NEG_MPPE},
3046 #endif
3047   {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
3048   "Deflate compression", "accept|deny|disable|enable",
3049   (const void *)NEG_DEFLATE},
3050   {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
3051   "Deflate (type 24) compression", "accept|deny|disable|enable",
3052   (const void *)NEG_PPPDDEFLATE},
3053   {"dns", NULL, NegotiateSet, LOCAL_AUTH,
3054   "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
3055   {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation",
3056   "accept|deny|disable|enable", (const void *)NEG_ENDDISC},
3057   {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3058   "Link Quality Reports", "accept|deny|disable|enable",
3059   (const void *)NEG_LQR},
3060   {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3061   "Password Authentication protocol", "accept|deny|disable|enable",
3062   (const void *)NEG_PAP},
3063   {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
3064   "Predictor 1 compression", "accept|deny|disable|enable",
3065   (const void *)NEG_PRED1},
3066   {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
3067   "Protocol field compression", "accept|deny|disable|enable",
3068   (const void *)NEG_PROTOCOMP},
3069   {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
3070   "MP Short Sequence Numbers", "accept|deny|disable|enable",
3071   (const void *)NEG_SHORTSEQ},
3072   {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
3073   "Van Jacobson header compression", "accept|deny|disable|enable",
3074   (const void *)NEG_VJCOMP},
3075   {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
3076   "Display this message", "accept|deny|disable|enable help|? [value]",
3077   NegotiateCommands},
3078   {NULL, NULL, NULL, 0, NULL, NULL, NULL},
3079 };
3080 
3081 static int
3082 NegotiateCommand(struct cmdargs const *arg)
3083 {
3084   if (arg->argc > arg->argn) {
3085     char const *argv[3];
3086     unsigned keep, add;
3087     int n;
3088 
3089     if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
3090       return -1;
3091     argv[2] = NULL;
3092 
3093     for (n = arg->argn; n < arg->argc; n++) {
3094       argv[1] = arg->argv[n];
3095       FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
3096                0 : NEG_OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
3097     }
3098   } else if (arg->prompt)
3099     prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
3100 	    arg->argv[arg->argn-1]);
3101   else
3102     log_Printf(LogWARN, "%s command must have arguments\n",
3103               arg->argv[arg->argn] );
3104 
3105   return 0;
3106 }
3107 
3108 const char *
3109 command_ShowNegval(unsigned val)
3110 {
3111   switch (val&3) {
3112     case 1: return "disabled & accepted";
3113     case 2: return "enabled & denied";
3114     case 3: return "enabled & accepted";
3115   }
3116   return "disabled & denied";
3117 }
3118 
3119 static int
3120 ClearCommand(struct cmdargs const *arg)
3121 {
3122   struct pppThroughput *t;
3123   struct datalink *cx;
3124   int i, clear_type;
3125 
3126   if (arg->argc < arg->argn + 1)
3127     return -1;
3128 
3129   if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
3130     cx = arg->cx;
3131     if (!cx)
3132       cx = bundle2datalink(arg->bundle, NULL);
3133     if (!cx) {
3134       log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
3135       return 1;
3136     }
3137     t = &cx->physical->link.stats.total;
3138   } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
3139     t = &arg->bundle->ncp.ipcp.throughput;
3140 #ifndef NOINET6
3141   else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0)
3142     t = &arg->bundle->ncp.ipv6cp.throughput;
3143 #endif
3144   else
3145     return -1;
3146 
3147   if (arg->argc > arg->argn + 1) {
3148     clear_type = 0;
3149     for (i = arg->argn + 1; i < arg->argc; i++)
3150       if (strcasecmp(arg->argv[i], "overall") == 0)
3151         clear_type |= THROUGHPUT_OVERALL;
3152       else if (strcasecmp(arg->argv[i], "current") == 0)
3153         clear_type |= THROUGHPUT_CURRENT;
3154       else if (strcasecmp(arg->argv[i], "peak") == 0)
3155         clear_type |= THROUGHPUT_PEAK;
3156       else
3157         return -1;
3158   } else
3159     clear_type = THROUGHPUT_ALL;
3160 
3161   throughput_clear(t, clear_type, arg->prompt);
3162   return 0;
3163 }
3164 
3165 static int
3166 RunListCommand(struct cmdargs const *arg)
3167 {
3168   const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
3169 
3170 #ifndef NONAT
3171   if (arg->cmd->args == NatCommands &&
3172       tolower(*arg->argv[arg->argn - 1]) == 'a') {
3173     if (arg->prompt)
3174       prompt_Printf(arg->prompt, "The alias command is deprecated\n");
3175     else
3176       log_Printf(LogWARN, "The alias command is deprecated\n");
3177   }
3178 #endif
3179 
3180   if (arg->argc > arg->argn)
3181     FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
3182              arg->prompt, arg->cx);
3183   else if (arg->prompt)
3184     prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
3185                   " <option>' for syntax help.\n", cmd, cmd);
3186   else
3187     log_Printf(LogWARN, "%s command must have arguments\n", cmd);
3188 
3189   return 0;
3190 }
3191 
3192 static int
3193 IfaceNameCommand(struct cmdargs const *arg)
3194 {
3195   int n = arg->argn;
3196 
3197   if (arg->argc != n + 1)
3198     return -1;
3199 
3200   if (!iface_Name(arg->bundle->iface, arg->argv[n]))
3201     return 1;
3202 
3203   log_SetTun(arg->bundle->unit, arg->bundle->iface->name);
3204   return 0;
3205 }
3206 
3207 static int
3208 IfaceAddCommand(struct cmdargs const *arg)
3209 {
3210   struct ncpaddr peer, addr;
3211   struct ncprange ifa;
3212   struct in_addr mask;
3213   int n, how;
3214 
3215   if (arg->argc == arg->argn + 1) {
3216     if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3217       return -1;
3218     ncpaddr_init(&peer);
3219   } else {
3220     if (arg->argc == arg->argn + 2) {
3221       if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
3222         return -1;
3223       n = 1;
3224     } else if (arg->argc == arg->argn + 3) {
3225       if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn]))
3226         return -1;
3227       if (ncpaddr_family(&addr) != AF_INET)
3228         return -1;
3229       ncprange_sethost(&ifa, &addr);
3230       if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1]))
3231         return -1;
3232       if (!ncpaddr_getip4(&addr, &mask))
3233         return -1;
3234       if (!ncprange_setip4mask(&ifa, mask))
3235         return -1;
3236       n = 2;
3237     } else
3238       return -1;
3239 
3240     if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n]))
3241       return -1;
3242 
3243     if (ncprange_family(&ifa) != ncpaddr_family(&peer)) {
3244       log_Printf(LogWARN, "IfaceAddCommand: src and dst address families"
3245                  " differ\n");
3246       return -1;
3247     }
3248   }
3249 
3250   how = IFACE_ADD_LAST;
3251   if (arg->cmd->args)
3252     how |= IFACE_FORCE_ADD;
3253 
3254   return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how);
3255 }
3256 
3257 static int
3258 IfaceDeleteCommand(struct cmdargs const *arg)
3259 {
3260   struct ncpaddr ifa;
3261   struct in_addr ifa4;
3262   int ok;
3263 
3264   if (arg->argc != arg->argn + 1)
3265     return -1;
3266 
3267   if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn]))
3268     return -1;
3269 
3270   if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
3271       ncpaddr_getip4(&ifa, &ifa4) &&
3272       arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) {
3273     log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
3274                ncpaddr_ntoa(&ifa));
3275     return 1;
3276   }
3277 
3278   ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa);
3279   if (!ok) {
3280     if (arg->cmd->args)
3281       ok = 1;
3282     else if (arg->prompt)
3283       prompt_Printf(arg->prompt, "%s: No such interface address\n",
3284                     ncpaddr_ntoa(&ifa));
3285     else
3286       log_Printf(LogWARN, "%s: No such interface address\n",
3287                  ncpaddr_ntoa(&ifa));
3288   }
3289 
3290   return !ok;
3291 }
3292 
3293 static int
3294 IfaceClearCommand(struct cmdargs const *arg)
3295 {
3296   int family, how;
3297 
3298   family = 0;
3299   if (arg->argc == arg->argn + 1) {
3300     if (strcasecmp(arg->argv[arg->argn], "inet") == 0)
3301       family = AF_INET;
3302 #ifndef NOINET6
3303     else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0)
3304       family = AF_INET6;
3305 #endif
3306     else
3307       return -1;
3308   } else if (arg->argc != arg->argn)
3309     return -1;
3310 
3311   how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
3312         arg->bundle->phys_type.all & PHYS_AUTO ?
3313         IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
3314   iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how);
3315 
3316   return 0;
3317 }
3318 
3319 static int
3320 SetProcTitle(struct cmdargs const *arg)
3321 {
3322   static char title[LINE_LEN];
3323   char *argv[MAXARGS];
3324   int argc = arg->argc - arg->argn;
3325 
3326   if (arg->argc <= arg->argn) {
3327     SetTitle(NULL);
3328     return 0;
3329   }
3330 
3331   if ((unsigned)argc >= sizeof argv / sizeof argv[0]) {
3332     argc = sizeof argv / sizeof argv[0] - 1;
3333     log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
3334   }
3335   command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
3336   Concatinate(title, sizeof title, argc, (const char *const *)argv);
3337   SetTitle(title);
3338   command_Free(argc, argv);
3339 
3340   return 0;
3341 }
3342