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