aboutsummaryrefslogtreecommitdiff
path: root/contrib/ldap/dhcpd-conf-to-ldap
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ldap/dhcpd-conf-to-ldap')
-rw-r--r--contrib/ldap/dhcpd-conf-to-ldap760
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 0000000..aee6c97
--- /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";
+