4 * Grabs any command line argument and processes any further options in
5 * the pptpd config file, before throwing over to pptpmanager.c.
7 * $Id: pptpd.c,v 1.18 2006/09/04 23:17:25 quozl Exp $
15 #define _GNU_SOURCE 1 /* strdup() prototype, broken arpa/inet.h */
19 #define __EXTENSIONS__ 1 /* strdup() prototype */
23 #define _XOPEN_SOURCE 500 /* strdup() prototype */
26 #include "our_syslog.h"
27 #include "our_getopt.h"
35 #include <sys/types.h>
36 #include <sys/socket.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
43 #include "configfile.h"
46 #include "pptpmanager.h"
49 #include <linux/ledman.h>
52 /* command line arg variables */
53 char *ppp_binary
= NULL
;
54 char *pppdoptstr
= NULL
;
55 char *speedstr
= NULL
;
56 char *bindaddr
= NULL
;
61 int pptp_noipparam
= 0;
63 int pptp_delegate
= 0;
65 int pptp_stimeout
= STIMEOUT_DEFAULT
;
67 int pptp_connections
= CONNECTIONS_DEFAULT
;
69 /* Local prototypes */
70 static void processIPStr(int type
, char *ipstr
);
73 static void my_daemon(int argc
, char **argv
);
76 static void log_pid(char *pid_file
);
77 static char *lookup(char *);
80 static void launch_bcrelay();
81 static void launch_bcrelay_br0();
82 static void launch_bcrelay_ppp();
83 static pid_t bcrelayfork
;
86 static void showusage(char *prog
)
88 printf("\npptpd v%s\n", VERSION
);
89 printf("Usage: pptpd [options], where options are:\n\n");
91 printf(" [-b] [--bcrelay if] Use broadcast relay for broadcasts comming from.\n");
92 printf(" the specified interface (default is eth1).\n");
94 printf(" [-c] [--conf file] Specifies the config file to read default\n");
95 printf(" settings from (default is %s).\n", PPTPD_CONFIG_FILE_DEFAULT
);
96 printf(" [-d] [--debug] Turns on debugging (to syslog).\n");
97 printf(" [-e] [--ppp file] Use alternate pppd binary, default %s.\n", PPP_BINARY
);
98 printf(" [-f] [--fg] Run in foreground.\n");
99 printf(" [-h] [--help] Displays this help message.\n");
100 printf(" [-i] [--noipparam] Suppress the passing of the client's IP address\n");
101 printf(" to PPP, which is done by default otherwise.\n");
102 printf(" [-l] [--listen x.x.x.x] Specifies IP of local interface to listen to.\n");
103 #if !defined(BSDUSER_PPP)
104 printf(" [-o] [--option file] Specifies the PPP options file to use\n");
105 printf(" (default is /etc/ppp/options).\n");
107 printf(" [-p] [--pidfile file] Specifies the file to write the process ID to\n");
108 printf(" (default is /var/run/pptpd.pid).\n");
109 #if !defined(BSDUSER_PPP)
110 printf(" [-s] [--speed baud] Specifies the baud speed for the PPP daemon\n");
111 printf(" (default is 115200).\n");
113 printf(" [-t] [--stimeout seconds] Specifies the timeout for the first packet. This is a DOS protection\n");
114 printf(" (default is 10).\n");
115 printf(" [-v] [--version] Displays the pptpd version number.\n");
116 printf(" [-w] [--logwtmp] Update wtmp as users login.\n");
117 printf(" [-C] [--connections n] Limit on number of connections.\n");
118 printf(" [-D] [--delegate] Delegate IP allocation to pppd.\n");
120 printf("\n\nLogs and debugging go to syslog as DAEMON.");
122 printf("\n\nCommand line options will override any default settings and any settings\n");
123 printf("specified in the config file (default config file: %s).\n\n", PPTPD_CONFIG_FILE_DEFAULT
);
127 static void showversion()
129 printf("pptpd v%s\n", VERSION
);
132 int main(int argc
, char **argv
)
134 /* command line options */
137 /* function-local options */
138 int foreground
= FALSE
;
139 char *pid_file
= NULL
;
142 char *configFile
= NULL
;
144 /* config file parsing temp strings */
145 char tmp
[MAX_CONFIG_STRING_SIZE
], *tmpstr
;
147 /* open a connection to the syslog daemon */
148 openlog("pptpd", LOG_PID
, PPTP_FACILITY
);
150 /* process command line options */
152 int option_index
= 0;
154 char *optstring
= "b:c:de:fhil:o:p:s:t:vwC:D";
156 char *optstring
= "c:de:fhil:o:p:s:t:vwC:D";
159 static struct option long_options
[] =
162 {"bcrelay", 1, 0, 0},
169 {"noipparam", 0, 0, 0},
172 {"pidfile", 1, 0, 0},
174 {"stimeout", 1, 0, 0},
175 {"version", 0, 0, 0},
176 {"logwtmp", 0, 0, 0},
177 {"connections", 1, 0, 0},
178 {"delegate", 0, 0, 0},
182 c
= getopt_long(argc
, argv
, optstring
, long_options
, &option_index
);
185 /* convert long options to short form */
188 c
= "bcdefhilopstvwCD"[option_index
];
190 c
= "cdefhilopstvwCD"[option_index
];
194 case 'b': /* --bcrelay */
195 if (bcrelay
) free(bcrelay
);
196 bcrelay
= strdup(optarg
);
200 case 'l': /* --listen */
201 tmpstr
= lookup(optarg
);
203 syslog(LOG_ERR
, "MGR: Invalid listening address: %s!", optarg
);
206 if (bindaddr
) free(bindaddr
);
207 bindaddr
= strdup(tmpstr
);
210 case 'h': /* --help */
214 case 'i': /* --noipparam */
215 pptp_noipparam
= TRUE
;
218 case 'e': /* --ppp */
219 if (ppp_binary
) free(ppp_binary
);
220 ppp_binary
= strdup(optarg
);
223 case 'd': /* --debug */
231 case 'v': /* --version */
235 case 'w': /* --logwtmp */
239 case 'C': /* --connections */
240 pptp_connections
= atoi(optarg
);
243 case 'D': /* --delegate */
244 pptp_delegate
= TRUE
;
247 case 'o': /* --option */
248 if (pppdoptstr
) free(pppdoptstr
);
249 pppdoptstr
= strdup(optarg
);
252 case 'p': /* --pidfile */
253 if (pid_file
) free(pid_file
);
254 pid_file
= strdup(optarg
);
257 case 's': /* --speed */
258 if (speedstr
) free(speedstr
);
259 speedstr
= strdup(optarg
);
262 case 't': /* --stimeout */
263 pptp_stimeout
= atoi(optarg
);
266 case 'c': /* --conf */
269 if (!(f
= fopen(optarg
, "r"))) {
270 syslog(LOG_ERR
, "MGR: Config file not found!");
274 if(configFile
) free(configFile
);
275 configFile
= strdup(optarg
);
285 /* Now that we have all the command line args.. lets open the
286 * conf file and add anything else (remembering not to override
287 * anything since the command line has more privilages :-)
291 configFile
= strdup(PPTPD_CONFIG_FILE_DEFAULT
);
293 if (read_config_file(configFile
, CONNECTIONS_KEYWORD
, tmp
) > 0) {
294 pptp_connections
= atoi(tmp
);
295 if (pptp_connections
<= 0)
296 pptp_connections
= CONNECTIONS_DEFAULT
;
299 slot_init(pptp_connections
);
301 if (!pptp_debug
&& read_config_file(configFile
, DEBUG_KEYWORD
, tmp
) > 0)
305 if (!bcrelay
&& read_config_file(configFile
, BCRELAY_KEYWORD
, tmp
) > 0)
306 bcrelay
= strdup(tmp
);
309 if (!pptp_stimeout
&& read_config_file(configFile
, STIMEOUT_KEYWORD
, tmp
) > 0) {
310 pptp_stimeout
= atoi(tmp
);
311 if (pptp_stimeout
<= 0)
312 pptp_stimeout
= STIMEOUT_DEFAULT
;
315 if (!pptp_noipparam
&& read_config_file(configFile
, NOIPPARAM_KEYWORD
, tmp
) > 0) {
316 pptp_noipparam
= TRUE
;
319 if (!bindaddr
&& read_config_file(configFile
, LISTEN_KEYWORD
, tmp
) > 0) {
320 tmpstr
= lookup(tmp
);
322 syslog(LOG_ERR
, "MGR: Invalid listening address: %s!", tmp
);
325 bindaddr
= strdup(tmpstr
);
328 if (!speedstr
&& read_config_file(configFile
, SPEED_KEYWORD
, tmp
) > 0)
329 speedstr
= strdup(tmp
);
331 if (!pppdoptstr
&& read_config_file(configFile
, PPPD_OPTION_KEYWORD
, tmp
) > 0) {
332 pppdoptstr
= strdup(tmp
);
335 if (!ppp_binary
&& read_config_file(configFile
, PPP_BINARY_KEYWORD
, tmp
) > 0) {
336 ppp_binary
= strdup(tmp
);
339 if (!pptp_logwtmp
&& read_config_file(configFile
, LOGWTMP_KEYWORD
, tmp
) > 0) {
343 if (!pptp_delegate
&& read_config_file(configFile
, DELEGATE_KEYWORD
, tmp
) > 0) {
344 pptp_delegate
= TRUE
;
348 pid_file
= strdup((read_config_file(configFile
, PIDFILE_KEYWORD
,
349 tmp
) > 0) ? tmp
: PIDFILE_DEFAULT
);
351 if (!pptp_delegate
) {
352 /* NOTE: remote then local, reason can be seen at the end of processIPStr */
354 /* grab the remoteip string from the config file */
355 if (read_config_file(configFile
, REMOTEIP_KEYWORD
, tmp
) <= 0) {
356 /* use "smart" defaults */
357 strlcpy(tmp
, DEFAULT_REMOTE_IP_LIST
, sizeof(tmp
));
359 processIPStr(REMOTE
, tmp
);
361 /* grab the localip string from the config file */
362 if (read_config_file(configFile
, LOCALIP_KEYWORD
, tmp
) <= 0) {
363 /* use "smart" defaults */
364 strlcpy(tmp
, DEFAULT_LOCAL_IP_LIST
, sizeof(tmp
));
366 processIPStr(LOCAL
, tmp
);
371 /* if not yet set, adopt default PPP binary path */
372 if (!ppp_binary
) ppp_binary
= strdup(PPP_BINARY
);
373 /* check that the PPP binary is executable */
374 if (access(ppp_binary
, X_OK
) < 0) {
375 syslog(LOG_ERR
, "MGR: PPP binary %s not executable",
379 /* check that the PPP options file is readable */
380 if (pppdoptstr
&& access(pppdoptstr
, R_OK
) < 0) {
381 syslog(LOG_ERR
, "MGR: PPP options file %s not readable",
386 /* check that the bcrelay binary is executable */
387 if (bcrelay
&& access(BCRELAY_BIN
, X_OK
) < 0) {
388 syslog(LOG_ERR
, "MGR: bcrelay binary %s not executable",
397 freopen("/dev/null", "r", stdin
);
399 /* returns to child only */
400 /* pid will have changed */
401 openlog("pptpd", LOG_PID
, PPTP_FACILITY
);
402 #else /* !HAVE_DAEMON */
403 my_daemon(argc
, argv
);
404 /* returns to child if !HAVE_FORK
405 * never returns if HAVE_FORK (re-execs with -f)
411 //Yau modified for dual way broadcast
412 if (strstr(bcrelay
, "br0")) {
413 syslog(LOG_DEBUG
, "CTRL: BCrelay incoming interface is %s", bcrelay
);
416 switch(bcrelayfork
= vfork()){
418 switch(bcrelayfork
= fork()){
420 case -1: /* fork() error */
421 syslog(LOG_ERR
, "CTRL: Error forking to exec bcrelay");
425 syslog(LOG_DEBUG
, "CTRL (BCrelay Launcher): Launching BCrelay with pid %i", bcrelayfork
);
426 launch_bcrelay_br0();
427 syslog(LOG_ERR
, "CTRL (BCrelay Launcher): Failed to launch BCrelay.");
431 if (strstr(bcrelay
, "ppp")) {
432 syslog(LOG_DEBUG
, "CTRL: BCrelay incoming interface is %s", bcrelay
);
435 switch(bcrelayfork
= vfork()){
437 switch(bcrelayfork
= fork()){
439 case -1: /* fork() error */
440 syslog(LOG_ERR
, "CTRL: Error forking to exec bcrelay");
444 syslog(LOG_DEBUG
, "CTRL (BCrelay Launcher): Launching BCrelay with pid %i", bcrelayfork
);
445 launch_bcrelay_ppp();
446 syslog(LOG_ERR
, "CTRL (BCrelay Launcher): Failed to launch BCrelay.");
453 /* turn the NETtel VPN LED on */
454 ledman_cmd(LEDMAN_CMD_ON
, LEDMAN_VPN
);
456 /* after we have our final pid... */
459 /* manage connections until SIGTERM */
460 pptp_manager(argc
, argv
);
463 if (bcrelayfork
> 0) {
464 syslog(LOG_DEBUG
, "CTRL: Closing child BCrelay with pid %i", bcrelayfork
);
465 kill(bcrelayfork
, SIGTERM
);
473 static void log_pid(char *pid_file
) {
478 if ((f
= fopen(pid_file
, "w")) == NULL
) {
479 syslog(LOG_ERR
, "PPTPD: failed to open(%s), errno=%d\n",
483 fprintf(f
, "%d\n", pid
);
488 static void my_daemon(int argc
, char **argv
)
491 /* need to use vfork - eg, uClinux */
494 extern char **environ
;
497 new_argv
= malloc((argc
+ 2) * sizeof(char **));
498 fdr
= open("/dev/null", O_RDONLY
);
499 syslog(LOG_INFO
, "MGR: Option parse OK, re-execing as daemon");
501 if ((pid
= vfork()) == 0) {
502 if (fdr
!= 0) { dup2(fdr
, 0); close(fdr
); }
506 memcpy(new_argv
+ 1, argv
, (argc
+ 1) * sizeof(char **));
507 new_argv
[0] = PPTPD_BIN
;
509 execve(PPTPD_BIN
, new_argv
, environ
);
511 } else if (pid
> 0) {
514 syslog_perror("vfork");
521 if ((pid
= fork()) < 0) {
522 syslog_perror("fork");
526 freopen("/dev/null", "r", stdin
);
530 /* pid will have changed */
531 openlog("pptpd", LOG_PID
, PPTP_FACILITY
);
536 /* added for hostname/address lookup -tmk
537 * returns NULL if not a valid hostname
539 static char *lookup(char *hostname
)
542 struct in_addr hst_addr
;
544 /* Try to parse IP directly */
545 if (inet_addr(hostname
) != -1)
548 /* Else lookup hostname, return NULL if it fails */
549 if ((ent
= gethostbyname(hostname
)) == NULL
)
552 /* That worked, print it back as a dotted quad. */
553 memcpy(&hst_addr
.s_addr
, ent
->h_addr
, ent
->h_length
);
554 return inet_ntoa(hst_addr
);
557 #define DEBUG_IP_PARSER 1
559 /* Return the address or NULL if not valid */
560 static char *validip(char *hostname
)
562 /* Try to parse IP directly */
563 if (inet_addr(hostname
) != -1)
569 /* Check if it's a valid IP range */
570 static int isIpRange(char *str
)
576 syslog(LOG_DEBUG
, "MGR: Checking if %s is a valid IP range", str
);
581 else if (*str
== '.')
583 else if (!strchr("0123456789", *str
)) {
585 syslog(LOG_DEBUG
, "MGR: Not an IP range: character %c is not valid", *str
);
591 syslog(LOG_DEBUG
, "MGR: Dashes = %d (wanted: 1), Dots = %d (wanted: 4)", dashes
, dots
);
593 return (dashes
== 1 && dots
== 3);
596 /* process a type 0 (LOCAL) or type 1 (REMOTE) IP string */
597 static void processIPStr(int type
, char *ipstr
)
602 /* char tmpstr2[20]; xxx.xxx.xxx.xxx-xxx (largest we can get) */
603 char tmpstr2
[128]; /* allow hostnames */
610 char ipa
[8]; /* xxx-xxx (largest we can get) */
615 char ip_pre
[13]; /* xxx.xxx.xxx. (largest we can get) */
621 int bail
= FALSE
; /* so we know when to stop formatting the ip line */
628 if ((tmpstr
= strchr(ipstr
, ',')) == NULL
) {
629 /* last (or only) entry reached */
630 strlcpy(tmpstr2
, ipstr
, sizeof(tmpstr2
));
633 pos
= tmpstr
- ipstr
;
635 strlcpy(tmpstr2
, ipstr
, sizeof(tmpstr2
));
640 syslog(LOG_DEBUG
, "MGR: Parsing segment: %s", tmpstr2
);
643 if (!isIpRange(tmpstr2
)) {
644 /* We got a normal IP
645 * Check if the IP address is valid, use it if so
647 if ((tmpstr7
= lookup(tmpstr2
)) == NULL
) {
648 syslog(LOG_ERR
, "MGR: Bad IP address (%s) in config file!", tmpstr2
);
651 if (num
== pptp_connections
) {
652 syslog(LOG_WARNING
, "MGR: connections limit (%d) reached, extra IP addresses ignored", pptp_connections
);
656 syslog(LOG_DEBUG
, "MGR: Setting IP %d = %s", num
, tmpstr7
);
659 slot_set_local(num
, tmpstr7
);
661 slot_set_remote(num
, tmpstr7
);
665 * eg. 192.168.0.234-238
666 * or (thanx Kev! :-).. i thought i was finished :-)
671 while ((tmpstr3
= strchr(tmpstr2
, '.')) != NULL
) {
672 pos
= tmpstr3
- tmpstr2
;
676 if ((tmpstr3
= strchr(tmpstr2
, '-')) == NULL
||
677 strchr(tmpstr3
+ 1, '-') != NULL
) {
678 syslog(LOG_ERR
, "MGR: Confused in IP parse routines (multiple hyphens)");
681 /* should be left with "192 168 0 234-238"
682 * or 192 168-178 1 231
685 sscanf(tmpstr2
, "%7s %7s %7s %7s", ipa
, ipb
, ipc
, ipd
);
687 if ((tmpstr6
= strchr(ipd
, '-')) != NULL
) {
690 sscanf(ipd
, "%3s %3s", ipl
, ipu
);
692 syslog(LOG_DEBUG
, "MGR: (lower upper) = (%s %s)", ipl
, ipu
);
697 syslog(LOG_DEBUG
, "MGR: Range = %d to %d on 4th segment", lower
, upper
);
699 sprintf(ip_pre
, "%.3s.%.3s.%.3s.", ipa
, ipb
, ipc
);
702 syslog(LOG_DEBUG
, "MGR: Pre = %s Post = %s", ip_pre
, ip_post
);
704 } else if ((tmpstr6
= strchr(ipc
, '-')) != NULL
) {
707 sscanf(ipc
, "%3s %3s", ipl
, ipu
);
709 syslog(LOG_DEBUG
, "MGR: (lower upper) = (%s %s)", ipl
, ipu
);
714 syslog(LOG_DEBUG
, "MGR: Range = %d to %d on 3rd segment", lower
, upper
);
716 sprintf(ip_pre
, "%.3s.%.3s.", ipa
, ipb
);
717 sprintf(ip_post
, ".%.3s", ipd
);
719 syslog(LOG_DEBUG
, "MGR: Pre = %s Post = %s", ip_pre
, ip_post
);
721 } else if ((tmpstr6
= strchr(ipb
, '-')) != NULL
) {
724 sscanf(ipb
, "%3s %3s", ipl
, ipu
);
726 syslog(LOG_DEBUG
, "MGR: (lower upper) = (%s %s)", ipl
, ipu
);
731 syslog(LOG_DEBUG
, "MGR: Range = %d to %d on 2nd segment", lower
, upper
);
733 sprintf(ip_pre
, "%.3s.", ipa
);
734 sprintf(ip_post
, ".%.3s.%.3s", ipc
, ipd
);
736 syslog(LOG_DEBUG
, "MGR: Pre = %s Post = %s", ip_pre
, ip_post
);
738 } else if ((tmpstr6
= strchr(ipa
, '-')) != NULL
) {
741 sscanf(ipa
, "%3s %3s", ipl
, ipu
);
743 syslog(LOG_DEBUG
, "MGR: (lower upper) = (%s %s)", ipl
, ipu
);
748 syslog(LOG_DEBUG
, "MGR: Range = %d to %d on 1st segment", lower
, upper
);
751 sprintf(ip_post
, ".%.3s.%.3s.%.3s", ipb
, ipc
, ipd
);
753 syslog(LOG_DEBUG
, "MGR: Pre = %s Post = %s", ip_pre
, ip_post
);
756 syslog(LOG_ERR
, "MGR: Confused in IP parse routines (lost hyphen)");
760 for (n
= lower
; n
<= upper
; n
++) {
761 sprintf(tmpstr5
, "%s%d%s", ip_pre
, n
, ip_post
);
762 /* Check if the ip address is valid */
763 if ((tmpstr7
= validip(tmpstr5
)) == NULL
) {
764 syslog(LOG_ERR
, "MGR: Bad IP address (%s) in config file!", tmpstr5
);
767 if (num
== pptp_connections
) {
768 syslog(LOG_WARNING
, "MGR: connections limit (%d) reached, extra IP addresses ignored", pptp_connections
);
772 syslog(LOG_DEBUG
, "MGR: Setting IP %d = %s", num
, tmpstr7
);
775 slot_set_local(num
, tmpstr7
);
777 slot_set_remote(num
, tmpstr7
);
782 if (num
== 1 && type
== LOCAL
&& pptp_connections
> 1) {
784 syslog(LOG_DEBUG
, "MGR: Setting all %d local IPs to %s", pptp_connections
, slot_get_local(0));
786 for (n
= 1; n
< pptp_connections
; n
++)
787 slot_set_local(n
, slot_get_local(0));
788 } else if (pptp_connections
> num
) {
789 syslog(LOG_INFO
, "MGR: Maximum of %d connections reduced to %d, not enough IP addresses given",
790 pptp_connections
, num
);
791 pptp_connections
= num
;
797 * Launches broadcast relay. Broadcast relay is responsible for relaying broadcasts to the clients
798 * retn: 0 on success, -1 on failure.
800 static void launch_bcrelay() {
801 char *bcrelay_argv
[8];
804 if (strstr(bcrelay
, "br0")) {
805 syslog(LOG_DEBUG
, "MGR: BCrelay incoming interface is %s", bcrelay
);
806 syslog(LOG_DEBUG
, "MGR: BCrelay outgoing interface is regexp ppp[4-9].*");
808 bcrelay_argv
[an
++] = BCRELAY_BIN
;
809 bcrelay_argv
[an
++] = "-i";
810 bcrelay_argv
[an
++] = bcrelay
;
811 bcrelay_argv
[an
++] = "-o";
812 bcrelay_argv
[an
++] = "ppp[4-9].*";
814 bcrelay_argv
[an
++] = "-n";
816 bcrelay_argv
[an
++] = NULL
;
818 execvp(bcrelay_argv
[0], bcrelay_argv
);
823 static void launch_bcrelay_br0() {
824 char *bcrelay_argv
[8];
827 if (strstr(bcrelay
, "br0")) {
828 syslog(LOG_DEBUG
, "MGR: BCrelay incoming interface is br0");
829 syslog(LOG_DEBUG
, "MGR: BCrelay outgoing interface is regexp ppp[4-9].*");
831 bcrelay_argv
[an
++] = BCRELAY_BIN
;
832 bcrelay_argv
[an
++] = "-i";
833 bcrelay_argv
[an
++] = "br0";
834 bcrelay_argv
[an
++] = "-o";
835 bcrelay_argv
[an
++] = "ppp[4-9].*";
837 bcrelay_argv
[an
++] = "-n";
839 bcrelay_argv
[an
++] = NULL
;
841 execvp(bcrelay_argv
[0], bcrelay_argv
);
845 static void launch_bcrelay_ppp() {
846 char *bcrelay_argv
[8];
849 if (strstr(bcrelay
, "ppp")) {
850 syslog(LOG_DEBUG
, "MGR: BCrelay incoming interface is regexp ppp[4-9].*");
851 syslog(LOG_DEBUG
, "MGR: BCrelay outgoing interface is br0");
853 bcrelay_argv
[an
++] = BCRELAY_BIN
;
854 bcrelay_argv
[an
++] = "-i";
855 bcrelay_argv
[an
++] = "ppp[4-9].*";
856 bcrelay_argv
[an
++] = "-o";
857 bcrelay_argv
[an
++] = "br0";
859 bcrelay_argv
[an
++] = "-n";
861 bcrelay_argv
[an
++] = NULL
;
863 execvp(bcrelay_argv
[0], bcrelay_argv
);