Project

General

Profile

Download (33.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/* $Id$ */
3
/*
4
	services.inc
5
	part of m0n0wall (http://m0n0.ch/wall)
6

    
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	All rights reserved.
9

    
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12

    
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15

    
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19

    
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30
*/
31

    
32
/* include all configuration functions */
33
require_once("functions.inc");
34

    
35
function load_balancer_use_sticky() {
36
	global $config, $g;
37
	if (isset ($config['system']['lb_use_sticky']))
38
		touch("/var/etc/use_pf_pool__stickyaddr");
39
	else
40
		unlink_if_exists("/var/etc/use_pf_pool__stickyaddr");
41
}
42

    
43
function services_dhcpd_configure() {
44
	global $config, $g;
45
	if(isset($config['system']['developerspew'])) {
46
		$mt = microtime();
47
		echo "services_dhcpd_configure($if) being called $mt\n";
48
	}
49

    
50
	/* if OLSRD is enabled, allow WAN to house DHCP. */
51
	if($config['installedpackages']['olsrd'])
52
		foreach($config['installedpackages']['olsrd']['config'] as $olsrd)
53
				if($olsrd['enable'])
54
					$is_olsr_enabled = true;
55

    
56
	/* configure DHCPD chroot */
57
	$fd = fopen("/tmp/dhcpd.sh","w");
58
	$status = `mount | grep "{$g['dhcpd_chroot_path']}/dev"`;
59
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}\n");
60
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/dev\n");
61
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/etc\n");
62
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/usr/local/sbin\n");
63
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/var/db\n");
64
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/usr\n");
65
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/lib\n");
66
	fwrite($fd, "mkdir -p {$g['dhcpd_chroot_path']}/run\n");
67
	fwrite($fd, "chown -R dhcpd:_dhcp {$g['dhcpd_chroot_path']}/*\n");
68
	fwrite($fd, "cp /lib/libc.so.6 {$g['dhcpd_chroot_path']}/lib/\n");
69
	fwrite($fd, "cp /usr/local/sbin/dhcpd {$g['dhcpd_chroot_path']}/usr/local/sbin/\n");
70
	fwrite($fd, "chmod a+rx {$g['dhcpd_chroot_path']}/usr/local/sbin/dhcpd\n");
71
	if(!trim($status))
72
		fwrite($fd, "mount_devfs devfs {$g['dhcpd_chroot_path']}/dev\n");
73
	fclose($fd);
74
	mwexec("/bin/sh /tmp/dhcpd.sh");
75

    
76
	/* kill any running dhcpd */
77
	if(is_process_running("dhcpd"))
78
		mwexec("killall dhcpd");
79

    
80
	$syscfg = $config['system'];
81
	$dhcpdcfg = $config['dhcpd'];
82

    
83
	/* DHCP enabled on any interfaces? */
84
	$dhcpdenable = false;
85
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
86
		if (isset($dhcpifconf['enable']) &&
87
			(($dhcpif == "lan") ||
88
			(isset($config['interfaces'][$dhcpif]['enable']) &&
89
			$config['interfaces'][$dhcpif]['if'] && (!$config['interfaces'][$dhcpif]['bridge']))))
90
			$dhcpdenable = true;
91
		if (isset($dhcpifconf['enable']) &&
92
			(($dhcpif == "wan") || (isset($config['interfaces'][$dhcpif]['enable']) &&
93
			$config['interfaces'][$dhcpif]['if'] && (!$config['interfaces'][$dhcpif]['bridge']))))
94
			$dhcpdenable = true;
95
	}
96

    
97
	if (!$dhcpdenable)
98
		return 0;
99

    
100
	if ($g['booting'])
101
		echo "Starting DHCP service...";
102
	else
103
		sleep(1);
104

    
105
	/* write dhcpd.conf */
106
	$fd = fopen("{$g['dhcpd_chroot_path']}/etc/dhcpd.conf", "w");
107
	if (!$fd) {
108
		printf("Error: cannot open dhcpd.conf in services_dhcpd_configure().\n");
109
		return 1;
110
	}
111

    
112

    
113

    
114
	$dhcpdconf = <<<EOD
115
option domain-name "{$syscfg['domain']}";
116
default-lease-time 7200;
117
max-lease-time 86400;
118
log-facility local7;
119
ddns-update-style none;
120
one-lease-per-client true;
121
deny duplicates;
122

    
123
EOD;
124

    
125
	$dhcpdifs = array();
126

    
127
	/*    loop through and deterimine if we need to setup
128
	 *    failover peer "bleh" entries
129
	 */
130
	$dhcpnum = 0;
131
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
132

    
133
		if(!isset($dhcpifconf['disableauthoritative']))
134
			$dhcpdconf .= "authoritative;\n";
135

    
136
		if($dhcpifconf['failover_peerip'] <> "") {
137
			/*
138
			 *    yep, failover peer is defined.
139
			 *    does it match up to a defined vip?
140
			 */
141
			$skew = 110;
142
			$a_vip = &$config['virtualip']['vip'];
143
			if(is_array($a_vip)) {
144
				foreach ($a_vip as $vipent) {
145
					$int = guess_interface_from_ip($dhcpifconf['failover_peerip']);
146
					$intip = find_interface_ip($int);
147
					$real_dhcpif = convert_friendly_interface_to_real_interface_name($dhcpif);
148
					if($int == $real_dhcpif) {
149
						/* this is the interface! */
150
						if($vipent['advskew'] < "20")
151
							$skew = 0;
152
					}
153
				}
154
			} else {
155
				log_error("Warning!  DHCP Failover setup and no CARP virtual IP's defined!");
156
			}
157
			if($skew > 10) {
158
				$type = "secondary";
159
				$dhcpdconf_pri  = "mclt 600;\n";
160
				$my_port = "520";
161
				$peer_port = "519";
162
			} else {
163
				$my_port = "519";
164
				$peer_port = "520";
165
				$type = "primary";
166
				$dhcpdconf_pri  = "split 128;\n";
167
				$dhcpdconf_pri .= "  mclt 600;\n";
168
			}
169
			$dhcpdconf .= <<<EOPP
170
failover peer "dhcp{$dhcpnum}" {
171
  {$type};
172
  address {$intip};
173
  port {$my_port};
174
  peer address {$dhcpifconf['failover_peerip']};
175
  peer port {$peer_port};
176
  max-response-delay 60;
177
  max-unacked-updates 10;
178
  {$dhcpdconf_pri}
179
  load balance max seconds 3;
180
}
181

    
182
EOPP;
183
		$dhcpnum++;
184
		}
185
	}
186

    
187
	$dhcpnum = 0;
188

    
189
	foreach ($dhcpdcfg as $dhcpif => $dhcpifconf) {
190

    
191
		$ifcfg = $config['interfaces'][$dhcpif];
192

    
193
		if (!isset($dhcpifconf['enable']) ||
194
			($ifcfg['ipaddr'] == "dhcp") ||
195
			(($dhcpif != "lan") &&
196
			(!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge'])))
197
			continue;
198

    
199
		if($dhcpif == "lan" && $ifcfg['bridge'])
200
			log_error("NOTE: DHCP Server on LAN is enabled.");
201

    
202
		$subnet = gen_subnet($ifcfg['ipaddr'], $ifcfg['subnet']);
203
		$subnetmask = gen_subnet_mask($ifcfg['subnet']);
204

    
205
		if($is_olsr_enabled == true)
206
			if($dhcpifconf['netmask'])
207
				$subnetmask = gen_subnet_mask($dhcpifconf['netmask']);
208

    
209
		$dnscfg = "";
210

    
211
		if ($dhcpifconf['domain']) {
212
			$dnscfg .= "	option domain-name \"{$dhcpifconf['domain']}\";\n";
213
		}
214
		if (isset($dhcpifconf['ddnsupdate'])) {
215
			if($dhcpifconf['ddnsdomain'] <> "") {
216
				$dnscfg .= "	ddns-domainname \"{$dhcpifconf['ddnsdomain']}\";\n";
217
			}
218
			$dnscfg .= "	ddns-update-style interim;\n";
219
		}
220

    
221

    
222
		if (is_array($dhcpifconf['dnsserver']) && ($dhcpifconf['dnsserver'][0])) {
223
			$dnscfg .= "	option domain-name-servers " . join(",", $dhcpifconf['dnsserver']) . ";";
224
		} else if (isset($config['dnsmasq']['enable'])) {
225
			$dnscfg .= "	option domain-name-servers " . $ifcfg['ipaddr'] . ";";
226
		} else if (is_array($syscfg['dnsserver']) && ($syscfg['dnsserver'][0])) {
227
			$dnscfg .= "	option domain-name-servers " . join(",", $syscfg['dnsserver']) . ";";
228
		}
229

    
230
		$dhcpdconf .= "subnet $subnet netmask $subnetmask {\n";
231
		$dhcpdconf .= "	pool {\n";
232

    
233
		/* is failover dns setup? */
234
		if (is_array($dhcpifconf['dnsserver']) && $dhcpifconf['dnsserver'][0] <> "") {
235
			$dhcpdconf .= "		option domain-name-servers {$dhcpifconf['dnsserver'][0]}";
236
			if($dhcpifconf['dnsserver'][1] <> "")
237
				$dhcpdconf .= ",{$dhcpifconf['dnsserver'][1]}";
238
			$dhcpdconf .= ";\n";
239
		}
240

    
241
		if($dhcpifconf['failover_peerip'] <> "")
242
			$dhcpdconf .= "		deny dynamic bootp clients;\n";
243

    
244
		if (isset($dhcpifconf['denyunknown']))
245
		   $dhcpdconf .= "		deny unknown clients;\n";
246

    
247
		if ($dhcpifconf['gateway'])
248
			$routers = $dhcpifconf['gateway'];
249
		else
250
			$routers = $ifcfg['ipaddr'];
251

    
252
		if($dhcpifconf['failover_peerip'] <> "") {
253
			$dhcpdconf .= "		failover peer \"dhcp{$dhcpnum}\";\n";
254
			$dhcpnum++;
255
		}
256

    
257
		$dhcpdconf .= <<<EOD
258
		range {$dhcpifconf['range']['from']} {$dhcpifconf['range']['to']};
259
	}
260
	option routers {$routers};
261
$dnscfg
262

    
263
EOD;
264

    
265
		if ($dhcpifconf['defaultleasetime'])
266
			$dhcpdconf .= "	default-lease-time {$dhcpifconf['defaultleasetime']};\n";
267
		if ($dhcpifconf['maxleasetime'])
268
			$dhcpdconf .= "	max-lease-time {$dhcpifconf['maxleasetime']};\n";
269

    
270
		if (is_array($dhcpifconf['winsserver']) && $dhcpifconf['winsserver'][0]) {
271
			$dhcpdconf .= "	option netbios-name-servers " . join(",", $dhcpifconf['winsserver']) . ";\n";
272
			$dhcpdconf .= "	option netbios-node-type 8;\n";
273
		}
274

    
275
		if (is_array($dhcpifconf['ntpserver']) && $dhcpifconf['ntpserver'][0])
276
			$dhcpdconf .= "	option ntp-servers " . join(",", $dhcpifconf['ntpserver']) . ";\n";
277

    
278
		if(isset($dhcpifconf['netboot'])) {
279
			if (($dhcpifconf['next-server'] <> "") && ($dhcpifconf['filename'] <> "")) {
280
				$dhcpdconf .= "	next-server {$dhcpifconf['next-server']};\n";
281
				$dhcpdconf .= "	filename \"{$dhcpifconf['filename']}\";\n";
282
			}
283
		}
284
		$dhcpdconf .= <<<EOD
285
}
286

    
287
EOD;
288

    
289
		/* add static mappings */
290
		if (is_array($dhcpifconf['staticmap'])) {
291

    
292
			$i = 0;
293
			foreach ($dhcpifconf['staticmap'] as $sm) {
294
				$dhcpdconf .= <<<EOD
295
host s_{$dhcpif}_{$i} {
296
	hardware ethernet {$sm['mac']};
297

    
298
EOD;
299
				if ($sm['ipaddr'])
300
					$dhcpdconf .= "	fixed-address {$sm['ipaddr']};\n";
301

    
302
				$dhcpdconf .= "}\n";
303
				$i++;
304
			}
305
		}
306

    
307
		$dhcpdifs[] = $ifcfg['if'];
308
	}
309

    
310
	fwrite($fd, $dhcpdconf);
311
	fclose($fd);
312

    
313
	/* create an empty leases database */
314
	touch("{$g['dhcpd_chroot_path']}/var/db/dhcpd.leases");
315

    
316
	/* fire up dhcpd in a chroot */
317
	mwexec("/usr/local/sbin/dhcpd -user dhcpd -group _dhcp -chroot {$g['dhcpd_chroot_path']} -cf {$g['dhcpd_chroot_path']}/etc/dhcpd.conf " .
318
		join(" ", $dhcpdifs));
319

    
320
	if ($g['booting']) {
321
		print "done.\n";
322
	}
323

    
324
	return 0;
325
}
326

    
327
function interfaces_staticarp_configure($if) {
328
	global $config, $g;
329
	if(isset($config['system']['developerspew'])) {
330
		$mt = microtime();
331
		echo "interfaces_staticarp_configure($if) being called $mt\n";
332
	}
333

    
334
        $ifcfg = $config['interfaces'][$if];
335

    
336
        /* Enable staticarp, if enabled */
337
        if(isset($config['dhcpd'][$if]['staticarp'])) {
338
                mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " staticarp " );
339
                mwexec("/usr/sbin/arp -ad > /dev/null 2>&1 ");
340
                if (is_array($config['dhcpd'][$if]['staticmap'])) {
341

    
342
                        foreach ($config['dhcpd'][$if]['staticmap'] as $arpent) {
343
                                mwexec("/usr/sbin/arp -s " . escapeshellarg($arpent['ipaddr']) . " " . escapeshellarg($arpent['mac']));
344

    
345
                        }
346

    
347
                }
348
        } else {
349
                mwexec("/sbin/ifconfig " . escapeshellarg($ifcfg['if']) . " -staticarp " );
350
                mwexec("/usr/sbin/arp -da > /dev/null 2>&1 ");
351
        }
352

    
353
        return 0;
354
}
355

    
356
function services_dhcrelay_configure() {
357
	global $config, $g;
358
	if(isset($config['system']['developerspew'])) {
359
		$mt = microtime();
360
		echo "services_dhcrelay_configure() being called $mt\n";
361
	}
362

    
363
	/* kill any running dhcrelay */
364
	killbypid("{$g['varrun_path']}/dhcrelay.pid");
365

    
366
	$dhcrelaycfg = $config['dhcrelay'];
367

    
368
	/* DHCPRelay enabled on any interfaces? */
369
	$dhcrelayenable = false;
370
	if(is_array($dhcrelaycfg)) {
371
		foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) {
372
			if (isset($dhcrelayifconf['enable']) &&
373
				(($dhcrelayif == "lan") ||
374
				(isset($config['interfaces'][$dhcrelayif]['enable']) &&
375
				$config['interfaces'][$dhcrelayif]['if'] && (!$config['interfaces'][$dhcrelayif]['bridge']))))
376
				$dhcrelayenable = true;
377
		}
378
	}
379

    
380
	if (!$dhcrelayenable)
381
		return 0;
382

    
383
	if ($g['booting'])
384
		echo "Starting DHCP relay service...";
385
	else
386
		sleep(1);
387

    
388
	$dhcrelayifs = array();
389
	foreach ($dhcrelaycfg as $dhcrelayif => $dhcrelayifconf) {
390

    
391
		$ifcfg = $config['interfaces'][$dhcrelayif];
392

    
393
		if (!isset($dhcrelayifconf['enable']) ||
394
			(($dhcrelayif != "lan") &&
395
			(!isset($ifcfg['enable']) || !$ifcfg['if'] || $ifcfg['bridge'])))
396
			continue;
397

    
398
		$dhcrelayifs[] = $ifcfg['if'];
399
	}
400

    
401
	/* In order for the relay to work, it needs to be active on the
402
	   interface in which the destination server sits */
403
	foreach ($config['interfaces'] as $ifname) {
404
		$subnet = $ifname['ipaddr'] . "/" . $ifname['subnet'];
405
		if (ip_in_subnet($dhcrelaycfg['server'],$subnet))
406
			$destif = $ifname['if'];
407
	}
408

    
409
	if (!isset($destif))
410
		$destif = $config['interfaces']['wan']['if'];
411

    
412
	$dhcrelayifs[] = $destif;
413
	$dhcrelayifs = array_unique($dhcrelayifs);
414

    
415
	/* fire up dhcrelay */
416
	$cmd = "/usr/local/sbin/dhcrelay -i " .  join(" -i ", $dhcrelayifs);
417

    
418
	if (isset($dhcrelaycfg['agentoption']))
419
		$cmd .=  " -a -m replace";
420

    
421
	$cmd .= " {$dhcrelaycfg['server']}";
422
	mwexec($cmd);
423

    
424
	if (!$g['booting']) {
425
		/* set the reload filter dity flag */
426
		touch("{$g['tmp_path']}/filter_dirty");
427
	}
428

    
429
	return 0;
430
}
431

    
432
function services_dyndns_reset() {
433
	global $config, $g;
434
	if(isset($config['system']['developerspew'])) {
435
		$mt = microtime();
436
		echo "services_dyndns_reset() being called $mt\n";
437
	}
438

    
439
	if (file_exists("{$g['vardb_path']}/ez-ipupdate.cache")) {
440
		conf_mount_rw();
441
		unlink("{$g['vardb_path']}/ez-ipupdate.cache");
442
		conf_mount_ro();
443
	}
444

    
445
	if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) {
446
		conf_mount_rw();
447
		unlink("{$g['conf_path']}/ez-ipupdate.cache");
448
		conf_mount_ro();
449
	}
450

    
451
	return 0;
452
}
453

    
454
function services_dyndns_configure() {
455
	global $config, $g;
456
	if(isset($config['system']['developerspew'])) {
457
		$mt = microtime();
458
		echo "services_dyndns_configure() being called $mt\n";
459
	}
460

    
461
	$dyndnscfg = $config['dyndns'];
462
	$wancfg = $config['interfaces']['wan'];
463

    
464
	if (isset($dyndnscfg['enable'])) {
465

    
466
		if ($g['booting']) {
467
			echo "Starting DynDNS client...";
468
			if(isset($config['system']['use_old_dyndns'])) {
469
				echo " [Using ez-ipupdate] ";
470
				services_dyndns_configure_old();
471
				return;
472
			}
473
		} else {
474
			sleep(1);
475
			if(isset($config['system']['use_old_dyndns'])) {
476
				services_dyndns_configure_old();
477
				return;
478
			}
479
		}
480

    
481
		/* load up the dyndns.class */
482
		require_once("dyndns.class");
483

    
484
		log_error("DynDns: Running updatedns()");
485

    
486
		/* determine WAN interface name */
487
		$wanif = get_real_wan_interface();
488
		/* get ip */
489
		$ip = find_interface_ip($wanif);
490

    
491
		$dns = new updatedns($dnsService = $config['dyndns']['type'],
492
							 $dnsHost = $config['dyndns']['host'],
493
							 $dnsUser = $config['dyndns']['username'],
494
							 $dnsPass = $config['dyndns']['password'],
495
							 $dnsWilcard = $config['dyndns']['wildcard'],
496
							 $dnsMX = $config['dyndns']['mx']);
497

    
498
		if ($g['booting'])
499
			echo "done.\n";
500
	}
501

    
502
	return 0;
503
}
504

    
505
function services_dyndns_configure_old() {
506
	global $config, $g;
507
	if(isset($config['system']['developerspew'])) {
508
		$mt = microtime();
509
		echo "services_dyndns_configure_old() being called $mt\n";
510
	}
511

    
512
        /* kill any running ez-ipupdate */
513
        /* ez-ipupdate needs SIGQUIT instead of SIGTERM */
514
        sigkillbypid("{$g['varrun_path']}/ez-ipupdate.pid", "QUIT");
515

    
516
        $dyndnscfg = $config['dyndns'];
517
        $wancfg = $config['interfaces']['wan'];
518

    
519
        if (isset($dyndnscfg['enable'])) {
520

    
521
                if ($g['booting'])
522
                        echo "Starting DynDNS client...";
523
                else
524
                        sleep(1);
525

    
526
                /* determine WAN interface name */
527
                $wanif = get_real_wan_interface();
528

    
529
                /* write ez-ipupdate.conf */
530
                $fd = fopen("{$g['varetc_path']}/ez-ipupdate.conf", "w");
531
                if (!$fd) {
532
                        printf("Error: cannot open ez-ipupdate.conf in services_dyndns_configure().\n");
533
                        return 1;
534
                }
535

    
536
                $ezipupdateconf = <<<EOD
537
service-type={$dyndnscfg['type']}
538
user={$dyndnscfg['username']}:{$dyndnscfg['password']}
539
host={$dyndnscfg['host']}
540
interface={$wanif}
541
max-interval=2073600
542
pid-file={$g['varrun_path']}/ez-ipupdate.pid
543
cache-file={$g['vardb_path']}/ez-ipupdate.cache
544
execute=/etc/rc.dyndns.storecache
545
daemon
546

    
547
EOD;
548

    
549
                /* enable server[:port]? */
550
                if ($dyndnscfg['server']) {
551
                        if ($dyndnscfg['port'])
552
                                $ezipupdateconf .= "server={$dyndnscfg['server']}:{$dyndnscfg['port']}\n";
553
                        else
554
                                $ezipupdateconf .= "server={$dyndnscfg['server']}\n";
555
                }
556

    
557
                /* enable MX? */
558
                if ($dyndnscfg['mx']) {
559
                        $ezipupdateconf .= "mx={$dyndnscfg['mx']}\n";
560
                }
561

    
562
                /* enable wildcards? */
563
                if (isset($dyndnscfg['wildcard'])) {
564
                        $ezipupdateconf .= "wildcard\n";
565
                }
566

    
567
                fwrite($fd, $ezipupdateconf);
568
                fclose($fd);
569

    
570
                /* if we're booting, copy the cache file from /conf */
571
                if ($g['booting']) {
572
                        if (file_exists("{$g['conf_path']}/ez-ipupdate.cache")) {
573
                                copy("{$g['conf_path']}/ez-ipupdate.cache", "{$g['vardb_path']}/ez-ipupdate.cache");
574
                       }
575
                }
576

    
577
                /* run ez-ipupdate */
578
                mwexec("/usr/local/bin/ez-ipupdate -c {$g['varetc_path']}/ez-ipupdate.conf");
579

    
580
                if ($g['booting'])
581
                        echo "done\n";
582
        }
583

    
584
        return 0;
585
}
586

    
587
function services_dnsmasq_configure() {
588
	global $config, $g;
589
	$return = 0;
590
	
591
	if(isset($config['system']['developerspew'])) {
592
		$mt = microtime();
593
		echo "services_dnsmasq_configure() being called $mt\n";
594
	}
595

    
596
	/* kill any running dnsmasq */
597
	sigkillbypid("{$g['varrun_path']}/dnsmasq.pid", "TERM");
598

    
599
	if (isset($config['dnsmasq']['enable'])) {
600

    
601
		if ($g['booting'])
602
			echo "Starting DNS forwarder...";
603
		else
604
			sleep(1);
605

    
606
		/* generate hosts file */
607
		if(system_hosts_generate()!=0)
608
			$return = 1;
609

    
610
		$args = "";
611

    
612
		if (isset($config['dnsmasq']['regdhcp'])) {
613

    
614
			$args .= " -l {$g['dhcpd_chroot_path']}/var/db/dhcpd.leases" .
615
				" -s {$config['system']['domain']}";
616
		}
617

    
618
                if (isset($config['dnsmasq']['domainoverrides']) && is_array($config['dnsmasq']['domainoverrides'])) {
619
                        foreach($config['dnsmasq']['domainoverrides'] as $override) {
620
                                $args .= ' --server=/' . $override['domain'] . '/' . $override['ip'];
621
                        }
622
                }
623

    
624
		/* suppose that dnsmasq handles our domain and don't send
625
		requests for our local domain to upstream servers */
626
		//if (!empty($config['system']['domain'])) {
627
		//	$args .= sprintf(' --local=/%s/', $config['system']['domain']);
628
		//}
629

    
630
		/* run dnsmasq */
631
		mwexec("/usr/local/sbin/dnsmasq {$args}");
632

    
633
		if ($g['booting'])
634
			echo "done.\n";
635
	}
636

    
637
	if (!$g['booting']) {
638
		if(services_dhcpd_configure()!=0)
639
			$return = 1;
640
	}
641

    
642
	return $return;
643
}
644

    
645
function services_snmpd_configure() {
646
	global $config, $g;
647
	if(isset($config['system']['developerspew'])) {
648
		$mt = microtime();
649
		echo "services_snmpd_configure() being called $mt\n";
650
	}
651

    
652
	/* kill any running snmpd */
653
	sigkillbypid("{$g['varrun_path']}/snmpd.pid", "TERM");
654
	if(is_process_running("bsnmpd")) 
655
		exec("/usr/bin/killall bsnmpd");
656

    
657
	if (isset($config['snmpd']['enable'])) {
658

    
659
		if ($g['booting'])
660
			echo "Starting SNMP daemon... ";
661

    
662
		/* generate snmpd.conf */
663
		$fd = fopen("{$g['varetc_path']}/snmpd.conf", "w");
664
		if (!$fd) {
665
			printf("Error: cannot open snmpd.conf in services_snmpd_configure().\n");
666
			return 1;
667
		}
668

    
669

    
670
		$snmpdconf = <<<EOD
671
location := "{$config['snmpd']['syslocation']}"
672
contact := "{$config['snmpd']['syscontact']}"
673
read := "{$config['snmpd']['rocommunity']}"
674

    
675
EOD;
676

    
677
/* No docs on what write strings do there for disable for now.
678
		if(isset($config['snmpd']['rwenable']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])){
679
		    $snmpdconf .= <<<EOD
680
# write string
681
write := "{$config['snmpd']['rwcommunity']}"
682

    
683
EOD;
684
		}
685
*/
686

    
687

    
688
		if(isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])){
689
		    $snmpdconf .= <<<EOD
690
# SNMP Trap support.
691
traphost := {$config['snmpd']['trapserver']}
692
trapport := {$config['snmpd']['trapserverport']}
693
trap := "{$config['snmpd']['trapstring']}"
694

    
695

    
696
EOD;
697
		}
698

    
699

    
700
		$snmpdconf .= <<<EOD
701
system := 1     # pfSense
702
%snmpd
703
begemotSnmpdDebugDumpPdus       = 2
704
begemotSnmpdDebugSyslogPri      = 7
705
begemotSnmpdCommunityString.0.1 = $(read)
706

    
707
EOD;
708

    
709
/* No docs on what write strings do there for disable for now.
710
		if(isset($config['snmpd']['rwcommunity']) && preg_match('/^\S+$/', $config['snmpd']['rwcommunity'])){
711
		    $snmpdconf .= <<<EOD
712
begemotSnmpdCommunityString.0.2 = $(write)
713

    
714
EOD;
715
		}
716
*/
717

    
718

    
719
		if(isset($config['snmpd']['trapenable']) && preg_match('/^\S+$/', $config['snmpd']['trapserver'])){
720
		    $snmpdconf .= <<<EOD
721
begemotTrapSinkStatus.[$(traphost)].$(trapport) = 4
722
begemotTrapSinkVersion.[$(traphost)].$(trapport) = 2
723
begemotTrapSinkComm.[$(traphost)].$(trapport) = $(trap)
724

    
725
EOD;
726
		}
727

    
728

    
729
		$snmpdconf .= <<<EOD
730
begemotSnmpdCommunityDisable    = 1
731

    
732
EOD;
733

    
734
		if(isset($config['snmpd']['bindlan'])) {
735
			$bind_to_ip = $config['interfaces']['lan']['ipaddr'];
736
		} else {
737
			$bind_to_ip = "0.0.0.0";
738
		}
739

    
740
		if(is_port( $config['snmpd']['pollport'] )) {
741
		    $snmpdconf .= <<<EOD
742
begemotSnmpdPortStatus.{$bind_to_ip}.{$config['snmpd']['pollport']} = 1
743

    
744
EOD;
745

    
746
		}
747

    
748
		$snmpdconf .= <<<EOD
749
begemotSnmpdLocalPortStatus."/var/run/snmpd.sock" = 1
750
begemotSnmpdLocalPortType."/var/run/snmpd.sock" = 4
751

    
752
# These are bsnmp macros not php vars.
753
sysContact      = $(contact)
754
sysLocation     = $(location)
755
sysObjectId     = 1.3.6.1.4.1.12325.1.1.2.1.$(system)
756

    
757
snmpEnableAuthenTraps = 2
758

    
759
EOD;
760

    
761
		if (is_array( $config['snmpd']['modules'] )) {
762
		    if(isset($config['snmpd']['modules']['mibii'])) {
763
			$snmpdconf .= <<<EOD
764
begemotSnmpdModulePath."mibII"  = "/usr/lib/snmp_mibII.so"
765

    
766
EOD;
767
		    }
768

    
769
		    if(isset($config['snmpd']['modules']['netgraph'])) {
770
			$snmpdconf .= <<<EOD
771
begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
772
%netgraph
773
begemotNgControlNodeName = "snmpd"
774

    
775
EOD;
776
		    }
777

    
778
		    if(isset($config['snmpd']['modules']['pf'])) {
779
			$snmpdconf .= <<<EOD
780
begemotSnmpdModulePath."pf"     = "/usr/lib/snmp_pf.so"
781

    
782
EOD;
783
		    }
784

    
785
		    if(isset($config['snmpd']['modules']['hostres'])) {
786
			$snmpdconf .= <<<EOD
787
begemotSnmpdModulePath."hostres"     = "/usr/lib/snmp_hostres.so"
788

    
789
EOD;
790
		    }
791
		    if(isset($config['snmpd']['modules']['bridge'])) {
792
			$snmpdconf .= <<<EOD
793
begemotSnmpdModulePath."bridge"     = "/usr/lib/snmp_bridge.so"
794
# config must end with blank line
795

    
796

    
797
EOD;
798
		    }
799
		}
800

    
801
		fwrite($fd, $snmpdconf);
802
		fclose($fd);
803

    
804
		if (isset($config['snmpd']['bindlan'])) {
805
			$bindlan = "";
806
		}
807

    
808
		/* run bsnmpd */
809
		mwexec("/usr/sbin/bsnmpd -c {$g['varetc_path']}/snmpd.conf" .
810
			"{$bindlan} -p {$g['varrun_path']}/snmpd.pid");
811

    
812
		if ($g['booting'])
813
			echo "done.\n";
814
	}
815

    
816
	return 0;
817
}
818

    
819
function services_proxyarp_configure() {
820
	global $config, $g;
821
	if(isset($config['system']['developerspew'])) {
822
		$mt = microtime();
823
		echo "services_proxyarp_configure() being called $mt\n";
824
	}
825

    
826
	/* kill any running choparp */
827
	killbyname("choparp");
828

    
829
	if (isset($config['virtualip']) && is_array($config['virtualip']['vip'])) {
830
		$paa = array();
831

    
832
		/* group by interface */
833
		foreach ($config['virtualip']['vip'] as $vipent) {
834
			if ($vipent['mode'] === "proxyarp") {
835
				if ($vipent['interface'])
836
					$if = $vipent['interface'];
837
				else
838
					$if = "wan";
839

    
840
				if (!is_array($paa[$if]))
841
					$paa[$if] = array();
842

    
843
				$paa[$if][] = $vipent;
844
			}
845
		}
846

    
847
		if (count($paa))
848
		foreach ($paa as $paif => $paents) {
849
			if ($paif == "wan" && !(is_ipaddr($config['interfaces']['wan']['ipaddr']) ||
850
                                       ($config['interfaces']['wan']['ipaddr'] == "dhcp") ||
851
                                       ($config['interfaces']['wan']['ipaddr'] == "bigpond")))
852
                               continue;
853

    
854
			$args = $config['interfaces'][$paif]['if'] . " auto";
855

    
856
			foreach ($paents as $paent) {
857

    
858
				if (isset($paent['subnet']))
859
					$args .= " " . escapeshellarg("{$paent['subnet']}/{$paent['subnet_bits']}");
860
				else if (isset($paent['range']))
861
					$args .= " " . escapeshellarg($paent['range']['from'] . "-" .
862
						$paent['range']['to']);
863
			}
864

    
865
			mwexec_bg("/usr/local/sbin/choparp " . $args);
866
		}
867
	}
868
}
869

    
870
function services_dnsupdate_process() {
871
	global $config, $g;
872
	if(isset($config['system']['developerspew'])) {
873
		$mt = microtime();
874
		echo "services_dnsupdate_process() being called $mt\n";
875
	}
876

    
877
	/* Dynamic DNS updating active? */
878
	if (isset($config['dnsupdate']['enable'])) {
879

    
880
		$wanip = get_current_wan_address();
881
		if ($wanip) {
882

    
883
			$keyname = $config['dnsupdate']['keyname'];
884
			/* trailing dot */
885
			if (substr($keyname, -1) != ".")
886
				$keyname .= ".";
887

    
888
			$hostname = $config['dnsupdate']['host'];
889
			/* trailing dot */
890
			if (substr($hostname, -1) != ".")
891
				$hostname .= ".";
892

    
893
			/* write private key file
894
			   this is dumb - public and private keys are the same for HMAC-MD5,
895
			   but nsupdate insists on having both */
896
			$fd = fopen("{$g['varetc_path']}/K{$keyname}+157+00000.private", "w");
897
			$privkey .= <<<EOD
898
Private-key-format: v1.2
899
Algorithm: 157 (HMAC)
900
Key: {$config['dnsupdate']['keydata']}
901

    
902
EOD;
903
			fwrite($fd, $privkey);
904
			fclose($fd);
905

    
906
			/* write public key file */
907
			if ($config['dnsupdate']['keytype'] == "zone") {
908
				$flags = 257;
909
				$proto = 3;
910
			} else if ($config['dnsupdate']['keytype'] == "host") {
911
				$flags = 513;
912
				$proto = 3;
913
			} else if ($config['dnsupdate']['keytype'] == "user") {
914
				$flags = 0;
915
				$proto = 2;
916
			}
917

    
918
			$fd = fopen("{$g['varetc_path']}/K{$keyname}+157+00000.key", "w");
919
			fwrite($fd, "{$keyname} IN KEY {$flags} {$proto} 157 {$config['dnsupdate']['keydata']}\n");
920
			fclose($fd);
921

    
922
			/* generate update instructions */
923
			$upinst =  "update delete {$config['dnsupdate']['host']} A\n";
924
			$upinst .= "update add {$config['dnsupdate']['host']} {$config['dnsupdate']['ttl']} A {$wanip}\n";
925
			$upinst .= "\n";	/* mind that trailing newline! */
926

    
927
			$fd = fopen("{$g['varetc_path']}/nsupdatecmds", "w");
928
			fwrite($fd, $upinst);
929
			fclose($fd);
930

    
931
			/* invoke nsupdate */
932
			$cmd = "/usr/sbin/nsupdate -k {$g['varetc_path']}/K{$keyname}+157+00000.key";
933
			if (isset($config['dnsupdate']['usetcp']))
934
				$cmd .= " -v";
935
			$cmd .= " {$g['varetc_path']}/nsupdatecmds";
936

    
937
			mwexec_bg($cmd);
938
		}
939
	}
940

    
941
	return 0;
942
}
943

    
944
function setup_wireless_olsr() {
945
	global $config, $g;
946
	if(!$config['installedpackages']['olsrd'] || !$config['installedpackages'])
947
		return;
948
	if(isset($config['system']['developerspew'])) {
949
		$mt = microtime();
950
		echo "setup_wireless_olsr($interface) being called $mt\n";
951
	}
952
	conf_mount_rw();
953
	foreach($config['installedpackages']['olsrd']['config'] as $olsrd) {
954
		$olsr_enable = $olsrd['enable'];
955
		if($olsr_enable <> "on")
956
			return;
957
		$fd = fopen("{$g['varetc_path']}/olsr.conf", "w");
958

    
959
		if($olsrd['announcedynamicroute'] or $olsrd['enableannounce'] == "on") {
960
			$enableannounce .= "\nHna4\n";
961
			$enableannounce .= "{\n";
962
		if($olsrd['announcedynamicroute'])
963
			$enableannounce .= "\t{$olsrd['announcedynamicroute']}\n";
964
		if($olsrd['enableannounce'] == "on")
965
			$enableannounce .= "0.0.0.0 0.0.0.0";
966
			$enableannounce .= "\n}\n";
967
		} else {
968
			$enableannounce = "";
969
		}
970

    
971
		$olsr .= <<<EODA
972
#
973
# olsr.org OLSR daemon config file
974
#
975
# Lines starting with a # are discarded
976
#
977
# This file was generated by setup_wireless_olsr() in services.inc
978
#
979

    
980
# This file is an example of a typical
981
# configuration for a mostly static
982
# network(regarding mobility) using
983
# the LQ extention
984

    
985
# Debug level(0-9)
986
# If set to 0 the daemon runs in the background
987

    
988
DebugLevel	2
989

    
990
# IP version to use (4 or 6)
991

    
992
IpVersion	4
993

    
994
# Clear the screen each time the internal state changes
995

    
996
ClearScreen     yes
997

    
998
{$enableannounce}
999

    
1000
# Should olsrd keep on running even if there are
1001
# no interfaces available? This is a good idea
1002
# for a PCMCIA/USB hotswap environment.
1003
# "yes" OR "no"
1004

    
1005
AllowNoInt	yes
1006

    
1007
# TOS(type of service) value for
1008
# the IP header of control traffic.
1009
# If not set it will default to 16
1010

    
1011
#TosValue	16
1012

    
1013
# The fixed willingness to use(0-7)
1014
# If not set willingness will be calculated
1015
# dynamically based on battery/power status
1016
# if such information is available
1017

    
1018
#Willingness    	4
1019

    
1020
# Allow processes like the GUI front-end
1021
# to connect to the daemon.
1022

    
1023
IpcConnect
1024
{
1025
     # Determines how many simultaneously
1026
     # IPC connections that will be allowed
1027
     # Setting this to 0 disables IPC
1028

    
1029
     MaxConnections  0
1030

    
1031
     # By default only 127.0.0.1 is allowed
1032
     # to connect. Here allowed hosts can
1033
     # be added
1034

    
1035
     Host            127.0.0.1
1036
     #Host            10.0.0.5
1037

    
1038
     # You can also specify entire net-ranges
1039
     # that are allowed to connect. Multiple
1040
     # entries are allowed
1041

    
1042
     #Net             192.168.1.0 255.255.255.0
1043
}
1044

    
1045
# Wether to use hysteresis or not
1046
# Hysteresis adds more robustness to the
1047
# link sensing but delays neighbor registration.
1048
# Used by default. 'yes' or 'no'
1049

    
1050
UseHysteresis	no
1051

    
1052
# Hysteresis parameters
1053
# Do not alter these unless you know
1054
# what you are doing!
1055
# Set to auto by default. Allowed
1056
# values are floating point values
1057
# in the interval 0,1
1058
# THR_LOW must always be lower than
1059
# THR_HIGH.
1060

    
1061
#HystScaling	0.50
1062
#HystThrHigh	0.80
1063
#HystThrLow	0.30
1064

    
1065

    
1066
# Link quality level
1067
# 0 = do not use link quality
1068
# 1 = use link quality for MPR selection
1069
# 2 = use link quality for MPR selection and routing
1070
# Defaults to 0
1071

    
1072
LinkQualityLevel	{$olsrd['enablelqe']}
1073

    
1074
# Link quality window size
1075
# Defaults to 10
1076

    
1077
LinkQualityWinSize	10
1078

    
1079
# Polling rate in seconds(float).
1080
# Default value 0.05 sec
1081

    
1082
Pollrate	0.05
1083

    
1084

    
1085
# TC redundancy
1086
# Specifies how much neighbor info should
1087
# be sent in TC messages
1088
# Possible values are:
1089
# 0 - only send MPR selectors
1090
# 1 - send MPR selectors and MPRs
1091
# 2 - send all neighbors
1092
#
1093
# defaults to 0
1094

    
1095
TcRedundancy	2
1096

    
1097
#
1098
# MPR coverage
1099
# Specifies how many MPRs a node should
1100
# try select to reach every 2 hop neighbor
1101
#
1102
# Can be set to any integer >0
1103
#
1104
# defaults to 1
1105

    
1106
MprCoverage	3
1107

    
1108
# Example plugin entry with parameters:
1109

    
1110
EODA;
1111

    
1112
if($olsrd['enablehttpinfo'] == "on") {
1113
	$olsr .= <<<EODB
1114

    
1115
LoadPlugin "/usr/local/lib/olsrd_httpinfo.so.0.1"
1116
{
1117
    PlParam     "port"   "{$olsrd['port']}"
1118
    PlParam     "Net"    "{$olsrd['allowedhttpinfohost']} {$olsrd['allowedhttpinfosubnet']}"
1119
}
1120

    
1121
EODB;
1122

    
1123
}
1124

    
1125
if($olsrd['enabledsecure'] == "on") {
1126
	$olsr .= <<<EODC
1127

    
1128
LoadPlugin "/usr/local/lib/olsrd_secure.so.0.5"
1129
{
1130
    PlParam     "Keyfile"   "/usr/local/etc/olsrkey.txt"
1131
}
1132

    
1133
EODC;
1134

    
1135
}
1136

    
1137
if($olsrd['enabledyngw'] == "on") {
1138

    
1139
	/* unset default route, olsr auto negotiates */
1140
	mwexec("/sbin/route delete default");
1141

    
1142
	$olsr .= <<<EODE
1143

    
1144
LoadPlugin "/usr/local/lib/olsrd_dyn_gw.so.0.4"
1145
{
1146
    # how often to look for a inet gw, in seconds
1147
    # defaults to 5 secs, if commented out
1148
    PlParam     "Interval"   "{$olsrd['polling']}"
1149

    
1150
    # if one or more IPv4 addresses are given, do a ping on these in
1151
    # descending order to validate that there is not only an entry in
1152
    # routing table, but also a real internet connection. If any of
1153
    # these addresses could be pinged successfully, the test was
1154
    # succesful, i.e. if the ping on the 1st address was successful,the
1155
    # 2nd won't be pinged
1156
    PlParam     "Ping"       "{$olsrd['ping']}"
1157
    #PlParam     "HNA"   "192.168.81.0 255.255.255.0"
1158
}
1159

    
1160
EODE;
1161

    
1162
}
1163

    
1164
foreach($config['installedpackages']['olsrd']['config'] as $conf) {
1165
	$interfaces = explode(',', $conf['iface_array']);
1166
	foreach($interfaces as $interface) {
1167
		$realinterface = convert_friendly_interface_to_real_interface_name($interface);
1168
$olsr .= <<<EODAD
1169
Interface "{$realinterface}"
1170
{
1171

    
1172
    # Hello interval in seconds(float)
1173
    HelloInterval    2.0
1174

    
1175
    # HELLO validity time
1176
    HelloValidityTime	20.0
1177

    
1178
    # TC interval in seconds(float)
1179
    TcInterval        5.0
1180

    
1181
    # TC validity time
1182
    TcValidityTime	30.0
1183

    
1184
    # MID interval in seconds(float)
1185
    MidInterval	5.0
1186

    
1187
    # MID validity time
1188
    MidValidityTime	30.0
1189

    
1190
    # HNA interval in seconds(float)
1191
    HnaInterval	5.0
1192

    
1193
    # HNA validity time
1194
    HnaValidityTime 	30.0
1195

    
1196
    # When multiple links exist between hosts
1197
    # the weight of interface is used to determine
1198
    # the link to use. Normally the weight is
1199
    # automatically calculated by olsrd based
1200
    # on the characteristics of the interface,
1201
    # but here you can specify a fixed value.
1202
    # Olsrd will choose links with the lowest value.
1203

    
1204
    # Weight 0
1205

    
1206

    
1207
}
1208

    
1209
EODAD;
1210

    
1211
	}
1212
	break;
1213
}
1214
		fwrite($fd, $olsr);
1215
		fclose($fd);
1216
	}
1217

    
1218
	if(is_process_running("olsrd"))
1219
		mwexec("/usr/bin/killall olsrd");
1220

    
1221
	sleep(2);
1222

    
1223
	mwexec_bg("/usr/local/sbin/olsrd -f {$g['varetc_path']}/olsr.conf");
1224

    
1225
	conf_mount_ro();
1226
}
1227

    
1228
/* configure cron service */
1229
function configure_cron() {
1230
	global $g, $config;
1231
	conf_mount_rw();
1232
	/* preserve existing crontab entries */
1233
	$crontab_contents = file_get_contents("/etc/crontab");
1234
	$crontab_contents_a = split("\n", $crontab_contents);
1235
	
1236
	for ($i = 0; $i < count($crontab_contents_a); $i++) {
1237
		$item =& $crontab_contents_a[$i];
1238
		if (strpos($item, "# pfSense specific crontab entries") !== false) {
1239
			array_splice($crontab_contents_a, $i - 1);
1240
			break;
1241
		}
1242
	}
1243
	$crontab_contents = implode("\n", $crontab_contents_a) . "\n";
1244
	
1245
	
1246
	if (is_array($config['cron']['item'])) {
1247
		$crontab_contents .= "#\n";
1248
		$crontab_contents .= "# pfSense specific crontab entries\n";
1249
		$crontab_contents .= "# Created: " . date("F j, Y, g:i a") . "\n";
1250
		$crontab_contents .= "#\n";
1251

    
1252
		foreach ($config['cron']['item'] as $item) {
1253
			$crontab_contents .= "\n{$item['minute']}\t";
1254
			$crontab_contents .= "{$item['hour']}\t";
1255
			$crontab_contents .= "{$item['mday']}\t";
1256
			$crontab_contents .= "{$item['month']}\t";
1257
			$crontab_contents .= "{$item['wday']}\t";
1258
			$crontab_contents .= "{$item['who']}\t";
1259
			$crontab_contents .= "{$item['command']}";
1260
		}
1261
    
1262
		$crontab_contents .= "\n#\n";
1263
		$crontab_contents .= "# If possible do not add items to this file manually.\n";
1264
		$crontab_contents .= "# If you do so, this file must be terminated with a blank line (e.g. new line)\n";
1265
		$crontab_contents .= "#\n\n";
1266
	}
1267
	
1268
	/* please maintain the newline at the end of file */
1269
	file_put_contents("/etc/crontab", $crontab_contents);
1270
	
1271
	if (!$g['booting'])
1272
		conf_mount_ro();
1273
}
1274

    
1275
?>
(19-19/29)