]>
git.proxmox.com Git - pve-container.git/blob - src/PVE/LXC.pm
3b97d8e87ad3566bc8ebe81b2c266a69588c7ffc
9 use PVE
:: Cluster
qw(cfs_register_file cfs_read_file) ;
12 use PVE
:: JSONSchema
qw(get_standard_option) ;
13 use PVE
:: Tools
qw( $IPV6RE $IPV4RE ) ;
17 cfs_register_file
( '/lxc/' , \
& parse_lxc_config
, \
& write_lxc_config
);
19 PVE
:: JSONSchema
:: register_format
( 'pve-lxc-network' , \
& verify_lxc_network
);
20 sub verify_lxc_network
{
21 my ( $value, $noerr ) = @_ ;
23 return $value if parse_lxc_network
( $value );
25 return undef if $noerr ;
27 die "unable to parse network setting \n " ;
30 my $nodename = PVE
:: INotify
:: nodename
();
33 my ( $name, $value ) = @_ ;
35 if ( $value =~ m/^(\d+)(b|k|m|g)?$/i ) {
36 my ( $res, $unit ) = ( $1, lc ( $2 || 'b' ));
38 return $res if $unit eq 'b' ;
39 return $res*1024 if $unit eq 'k' ;
40 return $res*1024*1024 if $unit eq 'm' ;
41 return $res*1024*1024*1024 if $unit eq 'g' ;
47 my $valid_lxc_keys = {
48 'lxc.arch' => 'i386|x86|i686|x86_64|amd64' ,
56 'lxc.cgroup.memory.limit_in_bytes' => \
& parse_lxc_size
,
57 'lxc.cgroup.memory.memsw.limit_in_bytes' => \
& parse_lxc_size
,
58 'lxc.cgroup.cpu.cfs_period_us' => '\d+' ,
59 'lxc.cgroup.cpu.cfs_quota_us' => '\d+' ,
60 'lxc.cgroup.cpu.shares' => '\d+' ,
64 'lxc.mount.entry' => 1 ,
65 'lxc.mount.auto' => 1 ,
70 'lxc.haltsignal' => 1 ,
71 'lxc.rebootsignal' => 1 ,
72 'lxc.stopsignal' => 1 ,
75 'lxc.console.logfile' => 1 ,
81 'lxc.aa_profile' => 1 ,
82 'lxc.aa_allow_incomplete' => 1 ,
83 'lxc.se_context' => 1 ,
86 'lxc.environment' => 1 ,
90 'lxc.start.auto' => 1 ,
91 'lxc.start.delay' => 1 ,
92 'lxc.start.order' => 1 ,
96 'lxc.hook.pre-start' => 1 ,
97 'lxc.hook.pre-mount' => 1 ,
98 'lxc.hook.mount' => 1 ,
99 'lxc.hook.autodev' => 1 ,
100 'lxc.hook.start' => 1 ,
101 'lxc.hook.post-stop' => 1 ,
102 'lxc.hook.clone' => 1 ,
105 'pve.nameserver' => sub {
106 my ( $name, $value ) = @_ ;
107 return verify_nameserver_list
( $value );
109 'pve.searchdomain' => sub {
110 my ( $name, $value ) = @_ ;
111 return verify_searchdomain_list
( $value );
113 'pve.onboot' => '(0|1)' ,
114 'pve.startup' => sub {
115 my ( $name, $value ) = @_ ;
116 return PVE
:: JSONSchema
:: pve_verify_startup_order
( $value );
121 my $valid_lxc_network_keys = {
124 name
=> 1 , # ifname inside container
125 'veth.pair' => 1 , # ifname at host (eth${vmid}.X)
129 my $valid_pve_network_keys = {
139 my $lxc_array_configs = {
145 sub write_lxc_config
{
146 my ( $filename, $data ) = @_ ;
150 return $raw if ! $data ;
152 my $done_hash = { digest
=> 1 };
154 foreach my $k ( sort keys %$data ) {
155 next if $k !~ m/^lxc\./ ;
156 $done_hash ->{ $k } = 1 ;
157 $raw .= " $k = $data ->{ $k } \n " ;
160 foreach my $k ( sort keys %$data ) {
161 next if $k !~ m/^pve\./ ;
162 $done_hash ->{ $k } = 1 ;
163 $raw .= " $k = $data ->{ $k } \n " ;
166 foreach my $k ( sort keys %$data ) {
167 next if $k !~ m/^net\d+$/ ;
168 $done_hash ->{ $k } = 1 ;
169 my $net = $data ->{ $k };
170 $raw .= "lxc.network.type = $net ->{type} \n " ;
171 foreach my $subkey ( sort keys %$net ) {
172 next if $subkey eq 'type' ;
173 if ( $valid_lxc_network_keys ->{ $subkey }) {
174 $raw .= "lxc.network. $subkey = $net ->{ $subkey } \n " ;
175 } elsif ( $valid_pve_network_keys ->{ $subkey }) {
176 $raw .= "pve.network. $subkey = $net ->{ $subkey } \n " ;
178 die "found invalid network key ' $subkey '" ;
183 foreach my $k ( sort keys %$data ) {
184 next if $done_hash ->{ $k };
185 die "found un-written value in config - implement this!" ;
191 sub parse_lxc_option
{
192 my ( $name, $value ) = @_ ;
194 my $parser = $valid_lxc_keys ->{ $name };
196 die "inavlid key ' $name ' \n " if ! defined ( $parser );
198 if ( $parser eq '1' ) {
200 } elsif ( ref ( $parser )) {
201 my $res = & $parser ( $name, $value );
202 return $res if defined ( $res );
205 return $value if $value =~ m/^$parser$/ ;
208 die "unable to parse value ' $value ' for option ' $name ' \n " ;
211 sub parse_lxc_config
{
212 my ( $filename, $raw ) = @_ ;
214 return undef if ! defined ( $raw );
217 digest
=> Digest
:: SHA
:: sha1_hex
( $raw ),
220 $filename =~ m
| /lxc/ ( \d
+)/ config
$|
221 || die "got strange filename ' $filename '" ;
225 my $network_counter = 0 ;
226 my $network_list = [];
227 my $host_ifnames = {};
229 my $find_next_hostif_name = sub {
230 for ( my $i = 0 ; $i < 10 ; $i++ ) {
231 my $name = "veth${vmid}. $i " ;
232 if (! $host_ifnames ->{ $name }) {
233 $host_ifnames ->{ $name } = 1 ;
238 die "unable to find free host_ifname" ; # should not happen
241 my $push_network = sub {
244 push @{ $network_list }, $netconf ;
246 if ( my $netname = $netconf ->{ 'veth.pair' }) {
247 if ( $netname =~ m/^veth(\d+).(\d)$/ ) {
248 die "wrong vmid for network interface pair \n " if $1 != $vmid ;
249 my $host_ifnames ->{ $netname } = 1 ;
251 die "wrong network interface pair \n " ;
258 while ( $raw && $raw =~ s/^(.*?)(\n|$)// ) {
261 next if $line =~ m/^\#/ ;
262 next if $line =~ m/^\s*$/ ;
264 if ( $line =~ m/^lxc\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
265 my ( $subkey, $value ) = ( $1, $2 );
266 if ( $subkey eq 'type' ) {
267 & $push_network ( $network );
268 $network = { type
=> $value };
269 } elsif ( $valid_lxc_network_keys ->{ $subkey }) {
270 $network ->{ $subkey } = $value ;
272 die "unable to parse config line: $line\n " ;
276 if ( $line =~ m/^pve\.network\.(\S+)\s*=\s*(\S+)\s*$/ ) {
277 my ( $subkey, $value ) = ( $1, $2 );
278 if ( $valid_pve_network_keys ->{ $subkey }) {
279 $network ->{ $subkey } = $value ;
281 die "unable to parse config line: $line\n " ;
285 if ( $line =~ m/^(pve.comment)\s*=\s*(\S.*)\s*$/ ) {
286 my ( $name, $value ) = ( $1, $2 );
287 $data ->{ $name } = $value ;
290 if ( $line =~ m/^((?:pve|lxc)\.\S+)\s*=\s*(\S.*)\s*$/ ) {
291 my ( $name, $value ) = ( $1, $2 );
293 die "multiple definitions for $name\n " if defined ( $data ->{ $name });
295 $data ->{ $name } = parse_lxc_option
( $name, $value );
299 die "unable to parse config line: $line\n " ;
302 & $push_network ( $network );
304 foreach my $net (@{ $network_list }) {
305 $net ->{ 'veth.pair' } = & $find_next_hostif_name () if ! $net ->{ 'veth.pair' };
306 $net ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $net ->{ hwaddr
};
307 die "unsupported network type ' $net ->{type}' \n " if $net ->{ type
} ne 'veth' ;
309 if ( $net ->{ 'veth.pair' } =~ m/^veth\d+.(\d+)$/ ) {
310 $data ->{ "net $1 " } = $net ;
318 my $vmlist = PVE
:: Cluster
:: get_vmlist
();
320 return $res if ! $vmlist || ! $vmlist ->{ ids
};
321 my $ids = $vmlist ->{ ids
};
323 foreach my $vmid ( keys %$ids ) {
324 next if ! $vmid ; # skip CT0
325 my $d = $ids ->{ $vmid };
326 next if ! $d ->{ node
} || $d ->{ node
} ne $nodename ;
327 next if ! $d ->{ type
} || $d ->{ type
} ne 'lxc' ;
328 $res ->{ $vmid }->{ type
} = 'lxc' ;
333 sub cfs_config_path
{
334 my ( $vmid, $node ) = @_ ;
336 $node = $nodename if ! $node ;
337 return "nodes/ $node/lxc/$vmid/config " ;
341 my ( $vmid, $node ) = @_ ;
343 my $cfspath = cfs_config_path
( $vmid, $node );
344 return "/etc/pve/ $cfspath " ;
350 my $cfspath = cfs_config_path
( $vmid );
352 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath );
353 die "container $vmid does not exists \n " if ! defined ( $conf );
359 my ( $vmid, $conf ) = @_ ;
361 my $cfspath = cfs_config_path
( $vmid );
363 PVE
:: Cluster
:: cfs_write_file
( $cfspath, $conf );
367 sub write_temp_config
{
368 my ( $vmid, $conf ) = @_ ;
371 my $filename = "/tmp/temp-lxc-conf- $vmid - $$ - $tempcounter .conf" ;
373 my $raw = write_lxc_config
( $filename, $conf );
375 PVE
:: Tools
:: file_set_contents
( $filename, $raw );
381 my ( $vmid, $timeout, $code, @param ) = @_ ;
383 my $lockdir = "/run/lock/lxc" ;
384 my $lockfile = " $lockdir/pve -config-{ $vmid }.lock" ;
386 File
:: Path
:: make_path
( $lockdir );
388 my $res = PVE
:: Tools
:: lock_file
( $lockfile, $timeout, $code, @param );
399 description
=> "Specifies whether a VM will be started during system bootup." ,
402 startup
=> get_standard_option
( 'pve-startup-order' ),
406 description
=> "The number of CPUs for this container (0 is unlimited)." ,
414 description
=> "CPU weight for a VM. Argument is used in the kernel fair scheduler. The larger the number is, the more CPU time this VM gets. Number is relative to weights of all the other running VMs. \n\n NOTE: You can disable fair-scheduler configuration by setting this to 0." ,
422 description
=> "Amount of RAM for the VM in MB." ,
429 description
=> "Amount of SWAP for the VM in MB." ,
436 description
=> "Amount of disk space for the VM in GB. A zero indicates no limits." ,
442 description
=> "Set a host name for the container." ,
449 description
=> "Container description. Only used on the configuration web interface." ,
454 description
=> "Sets DNS search domains for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
459 description
=> "Sets DNS server IP address for a container. Create will automatically use the setting from the host if you neither set searchdomain or nameserver." ,
463 my $MAX_LXC_NETWORKS = 10 ;
464 for ( my $i = 0 ; $i < $MAX_LXC_NETWORKS ; $i++ ) {
465 $confdesc ->{ "net $i " } = {
467 type
=> 'string' , format
=> 'pve-lxc-network' ,
468 description
=> "Specifies network interfaces for the container." ,
475 return defined ( $confdesc ->{ $name });
478 # add JSON properties for create and set function
479 sub json_config_properties
{
482 foreach my $opt ( keys %$confdesc ) {
483 $prop ->{ $opt } = $confdesc ->{ $opt };
489 # container status helpers
491 sub list_active_containers
{
493 my $filename = "/proc/net/unix" ;
495 # similar test is used by lcxcontainers.c: list_active_containers
498 my $fh = IO
:: File-
> new ( $filename, "r" );
501 while ( defined ( my $line = < $fh >)) {
502 if ( $line =~ m/^[a-f0-9]+:\s\S+\s\S+\s\S+\s\S+\s\S+\s\d+\s(\S+)$/ ) {
504 if ( $path =~ m!^@/etc/pve/lxc/(\d+)/command$! ) {
515 # warning: this is slow
519 my $active_hash = list_active_containers
();
521 return 1 if defined ( $active_hash ->{ $vmid });
529 my $list = $opt_vmid ?
{ $opt_vmid => { type
=> 'lxc' }} : config_list
();
531 my $active_hash = list_active_containers
();
533 foreach my $vmid ( keys %$list ) {
534 my $d = $list ->{ $vmid };
535 $d ->{ status
} = $active_hash ->{ $vmid } ?
'running' : 'stopped' ;
537 my $cfspath = cfs_config_path
( $vmid );
538 my $conf = PVE
:: Cluster
:: cfs_read_file
( $cfspath ) || {};
540 $d ->{ name
} = $conf ->{ 'lxc.utsname' } || "CT $vmid " ;
541 $d ->{ name
} =~ s/[\s]//g ;
545 my $cfs_period_us = $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
546 my $cfs_quota_us = $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
548 if ( $cfs_period_us && $cfs_quota_us ) {
549 $d ->{ cpus
} = int ( $cfs_quota_us/$cfs_period_us );
554 if ( my $private = $conf ->{ 'lxc.rootfs' }) {
555 my $res = PVE
:: Tools
:: df
( $private, 2 );
556 $d ->{ disk
} = $res ->{ used
};
557 $d ->{ maxdisk
} = $res ->{ total
};
562 $d ->{ maxmem
} = ( $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }|| 0 ) +
563 ( $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }|| 0 );
575 foreach my $vmid ( keys %$list ) {
576 my $d = $list ->{ $vmid };
577 next if $d ->{ status
} ne 'running' ;
579 $d ->{ uptime
} = 100 ; # fixme:
581 $d ->{ mem
} = read_cgroup_value
( 'memory' , $vmid, 'memory.usage_in_bytes' );
582 $d ->{ swap
} = read_cgroup_value
( 'memory' , $vmid, 'memory.memsw.usage_in_bytes' ) - $d ->{ mem
};
589 sub print_lxc_network
{
592 die "no network bridge defined \n " if ! $net ->{ bridge
};
594 my $res = "bridge= $net ->{bridge}" ;
596 foreach my $k ( qw(hwaddr mtu name ip gw ip6 gw6 firewall tag) ) {
597 next if ! defined ( $net ->{ $k });
598 $res .= ", $k = $net ->{ $k }" ;
604 sub parse_lxc_network
{
609 return $res if ! $data ;
611 foreach my $pv ( split ( /,/ , $data )) {
612 if ( $pv =~ m/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|firewall|tag)=(\S+)$/ ) {
619 $res ->{ type
} = 'veth' ;
620 $res ->{ hwaddr
} = PVE
:: Tools
:: random_ether_addr
() if ! $res ->{ mac
};
625 sub read_cgroup_value
{
626 my ( $group, $vmid, $name, $full ) = @_ ;
628 my $path = "/sys/fs/cgroup/ $group/lxc/$vmid/$name " ;
630 return PVE
:: Tools
:: file_get_contents
( $path ) if $full ;
632 return PVE
:: Tools
:: file_read_firstline
( $path );
635 sub find_lxc_console_pids
{
639 PVE
:: Tools
:: dir_glob_foreach
( '/proc' , '\d+' , sub {
642 my $cmdline = PVE
:: Tools
:: file_read_firstline
( "/proc/ $pid/cmdline " );
645 my @args = split ( /\0/ , $cmdline );
647 # serach for lxc-console -n <vmid>
648 return if scalar ( @args ) != 3 ;
649 return if $args [ 1 ] ne '-n' ;
650 return if $args [ 2 ] !~ m/^\d+$/ ;
651 return if $args [ 0 ] !~ m
|^( /usr/ bin
/) ?lxc-console
$|;
655 push @{ $res ->{ $vmid }}, $pid ;
661 my $ipv4_reverse_mask = [
697 # Note: we cannot use Net:IP, because that only allows strict
699 sub parse_ipv4_cidr
{
700 my ( $cidr, $noerr ) = @_ ;
702 if ( $cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ( $2 > 7 ) && ( $2 < 32 )) {
703 return { address
=> $1, netmask
=> $ipv4_reverse_mask ->[ $2 ] };
706 return undef if $noerr ;
708 die "unable to parse ipv4 address/mask \n " ;
711 sub lxc_conf_to_pve
{
712 my ( $vmid, $lxc_conf ) = @_ ;
714 my $properties = json_config_properties
();
716 my $conf = { digest
=> $lxc_conf ->{ digest
} };
718 foreach my $k ( keys %$properties ) {
720 if ( $k eq 'description' ) {
721 if ( my $raw = $lxc_conf ->{ 'pve.comment' }) {
722 $conf ->{ $k } = PVE
:: Tools
:: decode_text
( $raw );
724 } elsif ( $k eq 'onboot' ) {
725 $conf ->{ $k } = $lxc_conf ->{ 'pve.onboot' } if $lxc_conf ->{ 'pve.onboot' };
726 } elsif ( $k eq 'startup' ) {
727 $conf ->{ $k } = $lxc_conf ->{ 'pve.startup' } if $lxc_conf ->{ 'pve.startup' };
728 } elsif ( $k eq 'hostname' ) {
729 $conf ->{ $k } = $lxc_conf ->{ 'lxc.utsname' } if $lxc_conf ->{ 'lxc.utsname' };
730 } elsif ( $k eq 'nameserver' ) {
731 $conf ->{ $k } = $lxc_conf ->{ 'pve.nameserver' } if $lxc_conf ->{ 'pve.nameserver' };
732 } elsif ( $k eq 'searchdomain' ) {
733 $conf ->{ $k } = $lxc_conf ->{ 'pve.searchdomain' } if $lxc_conf ->{ 'pve.searchdomain' };
734 } elsif ( $k eq 'memory' ) {
735 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' }) {
736 $conf ->{ $k } = int ( $value / ( 1024 * 1024 ));
738 } elsif ( $k eq 'swap' ) {
739 if ( my $value = $lxc_conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' }) {
740 my $mem = $lxc_conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } || 0 ;
741 $conf ->{ $k } = int (( $value - $mem ) / ( 1024 * 1024 ));
743 } elsif ( $k eq 'cpus' ) {
744 my $cfs_period_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
745 my $cfs_quota_us = $lxc_conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
747 if ( $cfs_period_us && $cfs_quota_us ) {
748 $conf ->{ $k } = int ( $cfs_quota_us/$cfs_period_us );
752 } elsif ( $k eq 'cpuunits' ) {
753 $conf ->{ $k } = $lxc_conf ->{ 'lxc.cgroup.cpu.shares' } || 1024 ;
754 } elsif ( $k =~ m/^net\d$/ ) {
755 my $net = $lxc_conf ->{ $k };
757 $conf ->{ $k } = print_lxc_network
( $net );
764 # verify and cleanup nameserver list (replace \0 with ' ')
765 sub verify_nameserver_list
{
766 my ( $nameserver_list ) = @_ ;
769 foreach my $server ( PVE
:: Tools
:: split_list
( $nameserver_list )) {
770 PVE
:: JSONSchema
:: pve_verify_ip
( $server );
774 return join ( ' ' , @list );
777 sub verify_searchdomain_list
{
778 my ( $searchdomain_list ) = @_ ;
781 foreach my $server ( PVE
:: Tools
:: split_list
( $searchdomain_list )) {
782 # todo: should we add checks for valid dns domains?
786 return join ( ' ' , @list );
789 sub update_lxc_config
{
790 my ( $vmid, $conf, $running, $param, $delete ) = @_ ;
793 die "unable to modify config while container is running \n " if $running ;
795 if ( defined ( $delete )) {
796 foreach my $opt ( @$delete ) {
797 if ( $opt eq 'hostname' || $opt eq 'memory' ) {
798 die "unable to delete required option ' $opt ' \n " ;
799 } elsif ( $opt eq 'swap' ) {
800 delete $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' };
801 } elsif ( $opt eq 'description' ) {
802 delete $conf ->{ 'pve.comment' };
803 } elsif ( $opt eq 'onboot' ) {
804 delete $conf ->{ 'pve.onboot' };
805 } elsif ( $opt eq 'startup' ) {
806 delete $conf ->{ 'pve.startup' };
807 } elsif ( $opt eq 'nameserver' ) {
808 delete $conf ->{ 'pve.nameserver' };
809 } elsif ( $opt eq 'searchdomain' ) {
810 delete $conf ->{ 'pve.searchdomain' };
811 } elsif ( $opt =~ m/^net\d$/ ) {
812 delete $conf ->{ $opt };
819 foreach my $opt ( keys %$param ) {
820 my $value = $param ->{ $opt };
821 if ( $opt eq 'hostname' ) {
822 $conf ->{ 'lxc.utsname' } = $value ;
823 } elsif ( $opt eq 'onboot' ) {
824 $conf ->{ 'pve.onboot' } = $value ?
1 : 0 ;
825 } elsif ( $opt eq 'startup' ) {
826 $conf ->{ 'pve.startup' } = $value ;
827 } elsif ( $opt eq 'nameserver' ) {
828 my $list = verify_nameserver_list
( $value );
829 $conf ->{ 'pve.nameserver' } = $list ;
830 } elsif ( $opt eq 'searchdomain' ) {
831 my $list = verify_searchdomain_list
( $value );
832 $conf ->{ 'pve.searchdomain' } = $list ;
833 } elsif ( $opt eq 'memory' ) {
834 $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' } = $value*1024*1024 ;
835 } elsif ( $opt eq 'swap' ) {
836 my $mem = $conf ->{ 'lxc.cgroup.memory.limit_in_bytes' };
837 $mem = $param ->{ memory
}* 1024 * 1024 if $param ->{ memory
};
838 $conf ->{ 'lxc.cgroup.memory.memsw.limit_in_bytes' } = $mem + $value*1024*1024 ;
839 } elsif ( $opt eq 'cpus' ) {
841 my $cfs_period_us = 100000 ;
842 $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' } = $cfs_period_us ;
843 $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' } = $cfs_period_us*$value ;
845 delete $conf ->{ 'lxc.cgroup.cpu.cfs_period_us' };
846 delete $conf ->{ 'lxc.cgroup.cpu.cfs_quota_us' };
848 } elsif ( $opt eq 'cpuunits' ) {
849 $conf ->{ 'lxc.cgroup.cpu.shares' } = $value ;
850 } elsif ( $opt eq 'description' ) {
851 $conf ->{ 'pve.comment' } = PVE
:: Tools
:: encode_text
( $value );
852 } elsif ( $opt =~ m/^net(\d+)$/ ) {
854 my $net = PVE
:: LXC
:: parse_lxc_network
( $value );
855 $net ->{ 'veth.pair' } = "veth${vmid}. $netid " ;
856 $conf ->{ $opt } = $net ;
863 sub get_primary_ips
{
866 # return data from net0
868 my $net = $conf ->{ net0
};
869 return undef if ! $net ;
871 my $ipv4 = $net ->{ ip
};
872 $ipv4 =~ s!/\d+$!! if $ipv4 ;
873 my $ipv6 = $net ->{ ip
};
874 $ipv6 =~ s!/\d+$!! if $ipv6 ;
876 return ( $ipv4, $ipv6 );