diff options
Diffstat (limited to 'contrib/ldap/dhcpd-conf-to-ldap')
-rw-r--r-- | contrib/ldap/dhcpd-conf-to-ldap | 760 |
1 files changed, 760 insertions, 0 deletions
diff --git a/contrib/ldap/dhcpd-conf-to-ldap b/contrib/ldap/dhcpd-conf-to-ldap new file mode 100644 index 00000000..aee6c979 --- /dev/null +++ b/contrib/ldap/dhcpd-conf-to-ldap @@ -0,0 +1,760 @@ +#!/usr/bin/perl -w + +# Brian Masney <masneyb@gftp.org> +# To use this script, set your base DN below. Then run +# ./dhcpd-conf-to-ldap.pl < /path-to-dhcpd-conf/dhcpd.conf > output-file +# The output of this script will generate entries in LDIF format. You can use +# the slapadd command to add these entries into your LDAP server. You will +# definately want to double check that your LDAP entries are correct before +# you load them into LDAP. + +# This script does not do much error checking. Make sure before you run this +# that the DHCP server doesn't give any errors about your config file + +# FailOver notes: +# Failover is disabled by default, since it may need manually intervention. +# You can try the '--use=failover' option to see what happens :-) +# +# If enabled, the failover pool references will be written to LDIF output. +# The failover configs itself will be added to the dhcpServer statements +# and not to the dhcpService object (since this script uses only one and +# it may be usefull to have multiple service containers in failover mode). +# Further, this script does not check if primary or secondary makes sense, +# it simply converts what it gets... + +use Net::Domain qw(hostname hostfqdn hostdomain); +use Getopt::Long; + +my $domain = hostdomain(); # your.domain +my $basedn = "dc=".$domain; + $basedn =~ s/\./,dc=/g; # dc=your,dc=domain +my $server = hostname(); # hostname (nodename) +my $dhcpcn = 'DHCP Config'; # CN of DHCP config tree +my $dhcpdn = "cn=$dhcpcn, $basedn"; # DHCP config tree DN +my $second = ''; # secondary server DN / hostname +my $i_conf = ''; # dhcp.conf file to read or stdin +my $o_ldif = ''; # output ldif file name or stdout +my @use = (); # extended flags (failover) + +sub usage($;$) +{ + my $rc = shift; + my $err= shift; + + print STDERR "Error: $err\n\n" if(defined $err); + print STDERR <<__EOF_USAGE__; +usage: + $0 [options] < dhcpd.conf > dhcpd.ldif + +options: + + --basedn "dc=your,dc=domain" ("$basedn") + + --dhcpdn "dhcp config DN" ("$dhcpdn") + + --server "dhcp server name" ("$server") + + --second "secondary server or DN" ("$second") + + --conf "/path/to/dhcpd.conf" (default is stdin) + --ldif "/path/to/output.ldif" (default is stdout) + + --use "extended features" (see source comments) +__EOF_USAGE__ + exit($rc); +} + + +sub next_token +{ + local ($lowercase) = @_; + local ($token, $newline); + + do + { + if (!defined ($line) || length ($line) == 0) + { + $line = <>; + return undef if !defined ($line); + chop $line; + $line_number++; + $token_number = 0; + } + + $line =~ s/#.*//; + $line =~ s/^\s+//; + $line =~ s/\s+$//; + } + while (length ($line) == 0); + + if (($token, $newline) = $line =~ /^(.*?)\s+(.*)/) + { + if ($token =~ /^"/) { + #handle quoted token + if ($token !~ /"\s*$/) + { + ($tok, $newline) = $newline =~ /([^"]+")(.*)/; + $token .= " $tok"; + } + } + $line = $newline; + } + else + { + $token = $line; + $line = ''; + } + $token_number++; + + $token =~ y/[A-Z]/[a-z]/ if $lowercase; + + return ($token); +} + + +sub remaining_line +{ + local ($block) = shift || 0; + local ($tmp, $str); + + $str = ""; + while (defined($tmp = next_token (0))) + { + $str .= ' ' if !($str eq ""); + $str .= $tmp; + last if $tmp =~ /;\s*$/; + last if($block and $tmp =~ /\s*[}{]\s*$/); + } + + $str =~ s/;$//; + return ($str); +} + + +sub +add_dn_to_stack +{ + local ($dn) = @_; + + $current_dn = "$dn, $current_dn"; +} + + +sub +remove_dn_from_stack +{ + $current_dn =~ s/^.*?,\s*//; +} + + +sub +parse_error +{ + print "Parse error on line number $line_number at token number $token_number\n"; + exit (1); +} + + +sub +print_entry +{ + return if (scalar keys %curentry == 0); + + if (!defined ($curentry{'type'})) + { + $hostdn = "cn=$server, $basedn"; + print "dn: $hostdn\n"; + print "cn: $server\n"; + print "objectClass: top\n"; + print "objectClass: dhcpServer\n"; + print "dhcpServiceDN: $current_dn\n"; + if(grep(/FaIlOvEr/i, @use)) + { + foreach my $fo_peer (keys %failover) + { + next if(scalar(@{$failover{$fo_peer}}) <= 1); + print "dhcpStatements: failover peer $fo_peer { ", + join('; ', @{$failover{$fo_peer}}), "; }\n"; + } + } + print "\n"; + + print "dn: $current_dn\n"; + print "cn: $dhcpcn\n"; + print "objectClass: top\n"; + print "objectClass: dhcpService\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + print "dhcpPrimaryDN: $hostdn\n"; + if(grep(/FaIlOvEr/i, @use) and ($second ne '')) + { + print "dhcpSecondaryDN: $second\n"; + } + } + elsif ($curentry{'type'} eq 'subnet') + { + print "dn: $current_dn\n"; + print "cn: " . $curentry{'ip'} . "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpSubnet\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + + print "dhcpNetMask: " . $curentry{'netmask'} . "\n"; + if (defined ($curentry{'ranges'})) + { + foreach $statement (@{$curentry{'ranges'}}) + { + print "dhcpRange: $statement\n"; + } + } + } + elsif ($curentry{'type'} eq 'shared-network') + { + print "dn: $current_dn\n"; + print "cn: " . $curentry{'descr'} . "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpSharedNetwork\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + } + elsif ($curentry{'type'} eq 'group') + { + print "dn: $current_dn\n"; + print "cn: group", $curentry{'idx'}, "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpGroup\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + } + elsif ($curentry{'type'} eq 'host') + { + print "dn: $current_dn\n"; + print "cn: " . $curentry{'host'} . "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpHost\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + + if (defined ($curentry{'hwaddress'})) + { + $curentry{'hwaddress'} =~ y/[A-Z]/[a-z]/; + print "dhcpHWAddress: " . $curentry{'hwaddress'} . "\n"; + } + } + elsif ($curentry{'type'} eq 'pool') + { + print "dn: $current_dn\n"; + print "cn: pool", $curentry{'idx'}, "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpPool\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + + if (defined ($curentry{'ranges'})) + { + foreach $statement (@{$curentry{'ranges'}}) + { + print "dhcpRange: $statement\n"; + } + } + } + elsif ($curentry{'type'} eq 'class') + { + print "dn: $current_dn\n"; + print "cn: " . $curentry{'class'} . "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpClass\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + } + elsif ($curentry{'type'} eq 'subclass') + { + print "dn: $current_dn\n"; + print "cn: " . $curentry{'subclass'} . "\n"; + print "objectClass: top\n"; + print "objectClass: dhcpSubClass\n"; + if (defined ($curentry{'options'})) + { + print "objectClass: dhcpOptions\n"; + } + print "dhcpClassData: " . $curentry{'class'} . "\n"; + } + + if (defined ($curentry{'statements'})) + { + foreach $statement (@{$curentry{'statements'}}) + { + print "dhcpStatements: $statement\n"; + } + } + + if (defined ($curentry{'options'})) + { + foreach $statement (@{$curentry{'options'}}) + { + print "dhcpOption: $statement\n"; + } + } + + print "\n"; + undef (%curentry); +} + + +sub parse_netmask +{ + local ($netmask) = @_; + local ($i); + + if ((($a, $b, $c, $d) = $netmask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) != 4) + { + parse_error (); + } + + $num = (($a & 0xff) << 24) | + (($b & 0xff) << 16) | + (($c & 0xff) << 8) | + ($d & 0xff); + + for ($i=1; $i<=32 && $num & (1 << (32 - $i)); $i++) + { + } + $i--; + + return ($i); +} + + +sub parse_subnet +{ + local ($ip, $tmp, $netmask); + + print_entry () if %curentry; + + $ip = next_token (0); + parse_error () if !defined ($ip); + + $tmp = next_token (1); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq 'netmask'); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + $netmask = parse_netmask ($tmp); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + add_dn_to_stack ("cn=$ip"); + $curentry{'type'} = 'subnet'; + $curentry{'ip'} = $ip; + $curentry{'netmask'} = $netmask; + $cursubnet = $ip; + $curcounter{$ip} = { pool => 0, group => 0 }; +} + + +sub parse_shared_network +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $descr = next_token (0); + parse_error () if !defined ($descr); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + add_dn_to_stack ("cn=$descr"); + $curentry{'type'} = 'shared-network'; + $curentry{'descr'} = $descr; +} + + +sub parse_host +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $host = next_token (0); + parse_error () if !defined ($host); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + add_dn_to_stack ("cn=$host"); + $curentry{'type'} = 'host'; + $curentry{'host'} = $host; +} + + +sub parse_group +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + my $idx; + if(exists($curcounter{$cursubnet})) { + $idx = ++$curcounter{$cursubnet}->{'group'}; + } else { + $idx = ++$curcounter{''}->{'group'}; + } + + add_dn_to_stack ("cn=group".$idx); + $curentry{'type'} = 'group'; + $curentry{'idx'} = $idx; +} + + +sub parse_pool +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + my $idx; + if(exists($curcounter{$cursubnet})) { + $idx = ++$curcounter{$cursubnet}->{'pool'}; + } else { + $idx = ++$curcounter{''}->{'pool'}; + } + + add_dn_to_stack ("cn=pool".$idx); + $curentry{'type'} = 'pool'; + $curentry{'idx'} = $idx; +} + + +sub parse_class +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $class = next_token (0); + parse_error () if !defined ($class); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + $class =~ s/\"//g; + add_dn_to_stack ("cn=$class"); + $curentry{'type'} = 'class'; + $curentry{'class'} = $class; +} + + +sub parse_subclass +{ + local ($descr, $tmp); + + print_entry () if %curentry; + + $class = next_token (0); + parse_error () if !defined ($class); + + $subclass = next_token (0); + parse_error () if !defined ($subclass); + + $tmp = next_token (0); + parse_error () if !defined ($tmp); + parse_error () if !($tmp eq '{'); + + add_dn_to_stack ("cn=$subclass"); + $curentry{'type'} = 'subclass'; + $curentry{'class'} = $class; + $curentry{'subclass'} = $subclass; +} + + +sub parse_hwaddress +{ + local ($type, $hw, $tmp); + + $type = next_token (1); + parse_error () if !defined ($type); + + $hw = next_token (1); + parse_error () if !defined ($hw); + $hw =~ s/;$//; + + $curentry{'hwaddress'} = "$type $hw"; +} + + +sub parse_range +{ + local ($tmp, $str); + + $str = remaining_line (); + + if (!($str eq '')) + { + $str =~ s/;$//; + push (@{$curentry{'ranges'}}, $str); + } +} + + +sub parse_statement +{ + local ($token) = shift; + local ($str); + + if ($token eq 'option') + { + $str = remaining_line (); + push (@{$curentry{'options'}}, $str); + } + elsif($token eq 'failover') + { + $str = remaining_line (1); # take care on block + if($str =~ /[{]/) + { + my ($peername, @statements); + + parse_error() if($str !~ /^\s*peer\s+(.+?)\s+[{]\s*$/); + parse_error() if(($peername = $1) !~ /^\"?[^\"]+\"?$/); + + # + # failover config block found: + # e.g. 'failover peer "some-name" {' + # + if(not grep(/FaIlOvEr/i, @use)) + { + print STDERR "Warning: Failover config 'peer $peername' found!\n"; + print STDERR " Skipping it, since failover disabled!\n"; + print STDERR " You may try out --use=failover option.\n"; + } + + until($str =~ /[}]/ or $str eq "") + { + $str = remaining_line (1); + # collect all statements, except ending '}' + push(@statements, $str) if($str !~ /[}]/); + } + $failover{$peername} = [@statements]; + } + else + { + # + # pool reference to failover config is fine + # e.g. 'failover peer "some-name";' + # + if(not grep(/FaIlOvEr/i, @use)) + { + print STDERR "Warning: Failover reference '$str' found!\n"; + print STDERR " Skipping it, since failover disabled!\n"; + print STDERR " You may try out --use=failover option.\n"; + } + else + { + push (@{$curentry{'statements'}}, $token. " " . $str); + } + } + } + elsif($token eq 'zone') + { + $str = $token; + while($str !~ /}$/) { + $str .= ' ' . next_token (0); + } + push (@{$curentry{'statements'}}, $str); + } + elsif($token =~ /^(authoritative)[;]*$/) + { + push (@{$curentry{'statements'}}, $1); + } + else + { + $str = $token . " " . remaining_line (); + push (@{$curentry{'statements'}}, $str); + } +} + + +my $ok = GetOptions( + 'basedn=s' => \$basedn, + 'dhcpdn=s' => \$dhcpdn, + 'server=s' => \$server, + 'second=s' => \$second, + 'conf=s' => \$i_conf, + 'ldif=s' => \$o_ldif, + 'use=s' => \@use, + 'h|help|usage' => sub { usage(0); }, +); + +unless($server =~ /^\w+/) + { + usage(1, "invalid server name '$server'"); + } +unless($basedn =~ /^\w+=[^,]+/) + { + usage(1, "invalid base dn '$basedn'"); + } + +if($dhcpdn =~ /^cn=([^,]+)/i) + { + $dhcpcn = "$1"; + } +$second = '' if not defined $second; +unless($second eq '' or $second =~ /^cn=[^,]+\s*,\s*\w+=[^,]+/i) + { + if($second =~ /^cn=[^,]+$/i) + { + # relative DN 'cn=name' + $second = "$second, $basedn"; + } + elsif($second =~ /^\w+/) + { + # assume hostname only + $second = "cn=$second, $basedn"; + } + else + { + usage(1, "invalid secondary '$second'") + } + } + +usage(1) unless($ok); + +if($i_conf ne "" and -f $i_conf) + { + if(not open(STDIN, '<', $i_conf)) + { + print STDERR "Error: can't open conf file '$i_conf': $!\n"; + exit(1); + } + } +if($o_ldif ne "") + { + if(-e $o_ldif) + { + print STDERR "Error: output ldif name '$o_ldif' already exists!\n"; + exit(1); + } + if(not open(STDOUT, '>', $o_ldif)) + { + print STDERR "Error: can't open ldif file '$o_ldif': $!\n"; + exit(1); + } + } + + +print STDERR "Creating LDAP Configuration with the following options:\n"; +print STDERR "\tBase DN: $basedn\n"; +print STDERR "\tDHCP DN: $dhcpdn\n"; +print STDERR "\tServer DN: cn=$server, $basedn\n"; +print STDERR "\tSecondary DN: $second\n" + if(grep(/FaIlOvEr/i, @use) and $second ne ''); +print STDERR "\n"; + +my $token; +my $token_number = 0; +my $line_number = 0; +my %curentry; +my $cursubnet = ''; +my %curcounter = ( '' => { pool => 0, group => 0 } ); + +$current_dn = "$dhcpdn"; +$curentry{'descr'} = $dhcpcn; +$line = ''; +%failover = (); + +while (($token = next_token (1))) + { + if ($token eq '}') + { + print_entry () if %curentry; + if($current_dn =~ /.+?,\s*${dhcpdn}$/) { + # don't go below dhcpdn ... + remove_dn_from_stack (); + } + } + elsif ($token eq 'subnet') + { + parse_subnet (); + next; + } + elsif ($token eq 'shared-network') + { + parse_shared_network (); + next; + } + elsif ($token eq 'class') + { + parse_class (); + next; + } + elsif ($token eq 'subclass') + { + parse_subclass (); + next; + } + elsif ($token eq 'pool') + { + parse_pool (); + next; + } + elsif ($token eq 'group') + { + parse_group (); + next; + } + elsif ($token eq 'host') + { + parse_host (); + next; + } + elsif ($token eq 'hardware') + { + parse_hwaddress (); + next; + } + elsif ($token eq 'range') + { + parse_range (); + next; + } + else + { + parse_statement ($token); + next; + } + } + +close(STDIN) if($i_conf); +close(STDOUT) if($o_ldif); + +print STDERR "Done.\n"; + |