]> git.pld-linux.org Git - packages/dhcp.git/blob - dhcpd-conf-to-ldap
- x32 rebuild
[packages/dhcp.git] / dhcpd-conf-to-ldap
1 #!/usr/bin/perl -w
2
3 # Brian Masney <masneyb@ntelos.net>
4 # To use this script, set your base DN below. Then run 
5 # ./dhcpd-conf-to-ldap.pl < /path-to-dhcpd-conf/dhcpd.conf > output-file
6 # The output of this script will generate entries in LDIF format. You can use
7 # the slapadd command to add these entries into your LDAP server. You will
8 # definately want to double check that your LDAP entries are correct before
9 # you load them into LDAP.
10
11 # This script does not do much error checking. Make sure before you run this
12 # that the DHCP server doesn't give any errors about your config file
13
14 # FailOver notes:
15 #   Failover is disabled by default, since it may need manually intervention.
16 #   You can try the '--use=failover' option to see what happens :-)
17 #
18 #   If enabled, the failover pool references will be written to LDIF output.
19 #   The failover configs itself will be added to the dhcpServer statements
20 #   and not to the dhcpService object (since this script uses only one and
21 #   it may be usefull to have multiple service containers in failover mode).
22 #   Further, this script does not check if primary or secondary makes sense,
23 #   it simply converts what it gets...
24
25 use Net::Domain qw(hostname hostfqdn hostdomain);
26 use Getopt::Long;
27
28 my $domain = hostdomain();           # your.domain
29 my $basedn = "dc=".$domain;
30    $basedn =~ s/\./,dc=/g;           # dc=your,dc=domain
31 my $server = hostname();             # hostname (nodename)
32 my $dhcpcn = 'DHCP Config';          # CN of DHCP config tree
33 my $dhcpdn = "cn=$dhcpcn, $basedn";  # DHCP config tree DN
34 my $second = '';                     # secondary server DN / hostname
35 my $i_conf = '';                     # dhcp.conf file to read or stdin
36 my $o_ldif = '';                     # output ldif file name or stdout
37 my @use    = ();                     # extended flags (failover)
38
39 sub usage($;$)
40 {
41   my $rc = shift;
42   my $err= shift;
43
44   print STDERR "Error: $err\n\n" if(defined $err);
45   print STDERR <<__EOF_USAGE__;
46 usage: 
47   $0 [options] < dhcpd.conf > dhcpd.ldif
48
49 options:
50
51   --basedn  "dc=your,dc=domain"        ("$basedn")
52
53   --dhcpdn  "dhcp config DN"           ("$dhcpdn")
54
55   --server  "dhcp server name"         ("$server")
56
57   --second  "secondary server or DN"   ("$second")
58
59   --conf    "/path/to/dhcpd.conf"      (default is stdin)
60   --ldif    "/path/to/output.ldif"     (default is stdout)
61
62   --use     "extended features"        (see source comments)
63 __EOF_USAGE__
64   exit($rc);
65 }
66
67
68 sub next_token
69 {
70   local ($lowercase) = @_;
71   local ($token, $newline);
72
73   do 
74     {
75       if (!defined ($line) || length ($line) == 0)
76         {
77           $line = <>;
78           return undef if !defined ($line);
79           chop $line;
80           $line_number++;
81           $token_number = 0;
82         }
83
84       $line =~ s/#.*//;
85       $line =~ s/^\s+//;
86       $line =~ s/\s+$//;
87     }
88   while (length ($line) == 0);
89
90   if (($token, $newline) = $line =~ /^(.*?)\s+(.*)/)
91     {
92       if ($token =~ /^"/) {
93        #handle quoted token
94        if ($token !~ /"\s*$/)
95        {
96          ($tok, $newline)  = $newline =~ /([^"]+")(.*)/;
97          $token .= " $tok";
98         }
99       }
100       $line = $newline;
101     }
102   else
103     {
104       $token = $line;
105       $line = '';
106     }
107   $token_number++;
108
109   $token =~ y/[A-Z]/[a-z]/ if $lowercase;
110
111   return ($token);
112 }
113
114
115 sub remaining_line
116 {
117   local ($block) = shift || 0;
118   local ($tmp, $str);
119
120   $str = "";
121   while (defined($tmp = next_token (0)))
122     {
123       $str .= ' ' if !($str eq "");
124       $str .= $tmp;
125       last if $tmp =~ /;\s*$/;
126       last if($block and $tmp =~ /\s*[}{]\s*$/);
127     }
128
129   $str =~ s/;$//;
130   return ($str);
131 }
132
133
134 sub
135 add_dn_to_stack
136 {
137   local ($dn) = @_;
138
139   $current_dn = "$dn, $current_dn";
140 }
141
142
143 sub
144 remove_dn_from_stack
145 {
146   $current_dn =~ s/^.*?,\s*//;
147 }
148
149
150 sub
151 parse_error
152 {
153   print "Parse error on line number $line_number at token number $token_number\n";
154   exit (1);
155 }
156
157
158 sub
159 print_entry
160 {
161   return if (scalar keys %curentry == 0);
162
163   if (!defined ($curentry{'type'}))
164     {
165       $hostdn = "cn=$server, $basedn";
166       print "dn: $hostdn\n";
167       print "cn: $server\n";
168       print "objectClass: top\n";
169       print "objectClass: dhcpServer\n";
170       print "dhcpServiceDN: $current_dn\n";
171       if(grep(/FaIlOvEr/i, @use))
172         {
173           foreach my $fo_peer (keys %failover)
174             {
175               next if(scalar(@{$failover{$fo_peer}}) <= 1);
176               print "dhcpStatements: failover peer $fo_peer { ",
177                     join('; ', @{$failover{$fo_peer}}), "; }\n";
178             }
179         }
180       print "\n";
181
182       print "dn: $current_dn\n";
183       print "cn: $dhcpcn\n";
184       print "objectClass: top\n";
185       print "objectClass: dhcpService\n";
186       if (defined ($curentry{'options'}))
187         {
188           print "objectClass: dhcpOptions\n";
189         }
190       print "dhcpPrimaryDN: $hostdn\n";
191       if(grep(/FaIlOvEr/i, @use) and ($second ne ''))
192         {
193           print "dhcpSecondaryDN: $second\n";
194         }
195     }
196   elsif ($curentry{'type'} eq 'subnet')
197     {
198       print "dn: $current_dn\n";
199       print "cn: " . $curentry{'ip'} . "\n";
200       print "objectClass: top\n";
201       print "objectClass: dhcpSubnet\n";
202       if (defined ($curentry{'options'}))
203         {
204           print "objectClass: dhcpOptions\n";
205         }
206       
207       print "dhcpNetMask: " . $curentry{'netmask'} . "\n";
208       if (defined ($curentry{'ranges'}))
209         {
210           foreach $statement (@{$curentry{'ranges'}})
211             {
212               print "dhcpRange: $statement\n";
213             }
214         }
215     }
216   elsif ($curentry{'type'} eq 'shared-network')
217     {
218       print "dn: $current_dn\n";
219       print "cn: " . $curentry{'descr'} . "\n";
220       print "objectClass: top\n";
221       print "objectClass: dhcpSharedNetwork\n";
222       if (defined ($curentry{'options'}))
223         {
224           print "objectClass: dhcpOptions\n";
225         }
226     }
227   elsif ($curentry{'type'} eq 'group')
228     {
229       print "dn: $current_dn\n";
230       print "cn: group", $curentry{'idx'}, "\n";
231       print "objectClass: top\n";
232       print "objectClass: dhcpGroup\n";
233       if (defined ($curentry{'options'}))
234         {
235           print "objectClass: dhcpOptions\n";
236         }
237     }
238   elsif ($curentry{'type'} eq 'host')
239     {
240       print "dn: $current_dn\n";
241       print "cn: " . $curentry{'host'} . "\n";
242       print "objectClass: top\n";
243       print "objectClass: dhcpHost\n";
244       if (defined ($curentry{'options'}))
245         {
246           print "objectClass: dhcpOptions\n";
247         }
248
249       if (defined ($curentry{'hwaddress'}))
250         {
251           $curentry{'hwaddress'} =~ y/[A-Z]/[a-z]/;
252           print "dhcpHWAddress: " . $curentry{'hwaddress'} . "\n";
253         }
254     }
255   elsif ($curentry{'type'} eq 'pool')
256     {
257       print "dn: $current_dn\n";
258       print "cn: pool", $curentry{'idx'}, "\n";
259       print "objectClass: top\n";
260       print "objectClass: dhcpPool\n";
261       if (defined ($curentry{'options'}))
262         {
263           print "objectClass: dhcpOptions\n";
264         }
265
266       if (defined ($curentry{'ranges'}))
267         {
268           foreach $statement (@{$curentry{'ranges'}})
269             {
270               print "dhcpRange: $statement\n";
271             }
272         }
273     }
274   elsif ($curentry{'type'} eq 'class')
275     {
276       print "dn: $current_dn\n";
277       print "cn: " . $curentry{'class'} . "\n";
278       print "objectClass: top\n";
279       print "objectClass: dhcpClass\n";
280       if (defined ($curentry{'options'}))
281         {
282           print "objectClass: dhcpOptions\n";
283         }
284     }
285   elsif ($curentry{'type'} eq 'subclass')
286     {
287       print "dn: $current_dn\n";
288       print "cn: " . $curentry{'subclass'} . "\n";
289       print "objectClass: top\n";
290       print "objectClass: dhcpSubClass\n";
291       if (defined ($curentry{'options'}))
292         {
293           print "objectClass: dhcpOptions\n";
294         }
295       print "dhcpClassData: " . $curentry{'class'} . "\n";
296     }
297
298   if (defined ($curentry{'statements'}))
299     {
300       foreach $statement (@{$curentry{'statements'}})
301         {
302           print "dhcpStatements: $statement\n";
303         }
304     }
305
306   if (defined ($curentry{'options'}))
307     {
308       foreach $statement (@{$curentry{'options'}})
309         {
310           print "dhcpOption: $statement\n";
311         }
312     }
313
314   print "\n";
315   undef (%curentry);
316 }
317
318
319 sub parse_netmask
320 {
321   local ($netmask) = @_;
322   local ($i);
323
324   if ((($a, $b, $c, $d) = $netmask =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/) != 4)
325     {
326       parse_error ();
327     }
328
329   $num = (($a & 0xff) << 24) |
330          (($b & 0xff) << 16) |
331          (($c & 0xff) << 8) |
332           ($d & 0xff);
333
334   for ($i=1; $i<=32 && $num & (1 << (32 - $i)); $i++)
335     {
336     }
337   $i--;
338
339   return ($i);
340 }
341
342
343 sub parse_subnet
344 {
345   local ($ip, $tmp, $netmask);
346
347   print_entry () if %curentry;
348     
349   $ip = next_token (0);
350   parse_error () if !defined ($ip);
351
352   $tmp = next_token (1);
353   parse_error () if !defined ($tmp);
354   parse_error () if !($tmp eq 'netmask');
355
356   $tmp = next_token (0);
357   parse_error () if !defined ($tmp);
358   $netmask = parse_netmask ($tmp);
359
360   $tmp = next_token (0);
361   parse_error () if !defined ($tmp);
362   parse_error () if !($tmp eq '{');
363
364   add_dn_to_stack ("cn=$ip");
365   $curentry{'type'} = 'subnet';
366   $curentry{'ip'} = $ip;
367   $curentry{'netmask'} = $netmask;
368   $cursubnet = $ip;
369   $curcounter{$ip} = { pool  => 0, group => 0 };
370 }
371
372
373 sub parse_shared_network
374 {
375   local ($descr, $tmp);
376
377   print_entry () if %curentry;
378
379   $descr = next_token (0);
380   parse_error () if !defined ($descr);
381
382   $tmp = next_token (0);
383   parse_error () if !defined ($tmp);
384   parse_error () if !($tmp eq '{');
385
386   add_dn_to_stack ("cn=$descr");
387   $curentry{'type'} = 'shared-network';
388   $curentry{'descr'} = $descr;
389 }
390
391
392 sub parse_host
393 {
394   local ($descr, $tmp);
395
396   print_entry () if %curentry;
397
398   $host = next_token (0);
399   parse_error () if !defined ($host);
400
401   $tmp = next_token (0);
402   parse_error () if !defined ($tmp);
403   parse_error () if !($tmp eq '{');
404
405   add_dn_to_stack ("cn=$host");
406   $curentry{'type'} = 'host';
407   $curentry{'host'} = $host;
408 }
409
410
411 sub parse_group
412 {
413   local ($descr, $tmp);
414
415   print_entry () if %curentry;
416
417   $tmp = next_token (0);
418   parse_error () if !defined ($tmp);
419   parse_error () if !($tmp eq '{');
420
421   my $idx;
422   if(exists($curcounter{$cursubnet})) {
423     $idx = ++$curcounter{$cursubnet}->{'group'};
424   } else {
425     $idx = ++$curcounter{''}->{'group'};
426   }
427
428   add_dn_to_stack ("cn=group".$idx);
429   $curentry{'type'} = 'group';
430   $curentry{'idx'} = $idx;
431 }
432
433
434 sub parse_pool
435 {
436   local ($descr, $tmp);
437
438   print_entry () if %curentry;
439
440   $tmp = next_token (0);
441   parse_error () if !defined ($tmp);
442   parse_error () if !($tmp eq '{');
443
444   my $idx;
445   if(exists($curcounter{$cursubnet})) {
446     $idx = ++$curcounter{$cursubnet}->{'pool'};
447   } else {
448     $idx = ++$curcounter{''}->{'pool'};
449   }
450
451   add_dn_to_stack ("cn=pool".$idx);
452   $curentry{'type'} = 'pool';
453   $curentry{'idx'} = $idx;
454 }
455
456
457 sub parse_class
458 {
459   local ($descr, $tmp);
460
461   print_entry () if %curentry;
462
463   $class = next_token (0);
464   parse_error () if !defined ($class);
465
466   $tmp = next_token (0);
467   parse_error () if !defined ($tmp);
468   parse_error () if !($tmp eq '{');
469
470   $class =~ s/\"//g;
471   add_dn_to_stack ("cn=$class");
472   $curentry{'type'} = 'class';
473   $curentry{'class'} = $class;
474 }
475
476
477 sub parse_subclass
478 {
479   local ($descr, $tmp);
480
481   print_entry () if %curentry;
482
483   $class = next_token (0);
484   parse_error () if !defined ($class);
485
486   $subclass = next_token (0);
487   parse_error () if !defined ($subclass);
488
489   $tmp = next_token (0);
490   parse_error () if !defined ($tmp);
491   parse_error () if !($tmp eq '{');
492
493   add_dn_to_stack ("cn=$subclass");
494   $curentry{'type'} = 'subclass';
495   $curentry{'class'} = $class;
496   $curentry{'subclass'} = $subclass;
497 }
498
499
500 sub parse_hwaddress
501 {
502   local ($type, $hw, $tmp);
503
504   $type = next_token (1);
505   parse_error () if !defined ($type);
506
507   $hw = next_token (1);
508   parse_error () if !defined ($hw);
509   $hw =~ s/;$//;
510
511   $curentry{'hwaddress'} = "$type $hw";
512 }
513
514     
515 sub parse_range
516 {
517   local ($tmp, $str);
518
519   $str = remaining_line ();
520
521   if (!($str eq ''))
522     {
523       $str =~ s/;$//;
524       push (@{$curentry{'ranges'}}, $str);
525     }
526 }
527
528
529 sub parse_statement
530 {
531   local ($token) = shift;
532   local ($str);
533
534   if ($token eq 'option')
535     {
536       $str = remaining_line ();
537       push (@{$curentry{'options'}}, $str);
538     }
539   elsif($token eq 'failover')
540     {
541       $str = remaining_line (1); # take care on block
542       if($str =~ /[{]/)
543         {
544           my ($peername, @statements);
545
546           parse_error() if($str !~ /^\s*peer\s+(.+?)\s+[{]\s*$/);
547           parse_error() if(($peername = $1) !~ /^\"?[^\"]+\"?$/);
548
549           #
550           # failover config block found:
551           # e.g. 'failover peer "some-name" {'
552           #
553           if(not grep(/FaIlOvEr/i, @use))
554             {
555               print STDERR "Warning: Failover config 'peer $peername' found!\n";
556               print STDERR "         Skipping it, since failover disabled!\n";
557               print STDERR "         You may try out --use=failover option.\n";
558             }
559
560           until($str =~ /[}]/ or $str eq "")
561             {
562                 $str = remaining_line (1);
563                 # collect all statements, except ending '}'
564                 push(@statements, $str) if($str !~ /[}]/);
565             }
566           $failover{$peername} = [@statements];
567         }
568       else
569         {
570           #
571           # pool reference to failover config is fine
572           # e.g. 'failover peer "some-name";'
573           #
574           if(not grep(/FaIlOvEr/i, @use))
575             {
576               print STDERR "Warning: Failover reference '$str' found!\n";
577               print STDERR "         Skipping it, since failover disabled!\n";
578               print STDERR "         You may try out --use=failover option.\n";
579             }
580           else
581             {
582               push (@{$curentry{'statements'}}, $token. " " . $str);
583             }
584         }
585     }
586   elsif($token eq 'zone')
587     {
588       $str = $token;
589       while($str !~ /}$/) {
590         $str .= ' ' . next_token (0);
591       }
592       push (@{$curentry{'statements'}}, $str);
593     }
594   elsif($token =~ /^(authoritative)[;]*$/)
595     {
596       push (@{$curentry{'statements'}}, $1);
597     }
598   else
599     {
600       $str = $token . " " . remaining_line ();
601       push (@{$curentry{'statements'}}, $str);
602     }
603 }
604
605
606 my $ok = GetOptions(
607     'basedn=s'      => \$basedn,
608     'dhcpdn=s'      => \$dhcpdn,
609     'server=s'      => \$server,
610     'second=s'      => \$second,
611     'conf=s'        => \$i_conf,
612     'ldif=s'        => \$o_ldif,
613     'use=s'         => \@use,
614     'h|help|usage'  => sub { usage(0); },
615 );
616
617 unless($server =~ /^\w+/)
618   {
619     usage(1, "invalid server name '$server'");
620   }
621 unless($basedn =~ /^\w+=[^,]+/)
622   {
623     usage(1, "invalid base dn '$basedn'");
624   }
625
626 if($dhcpdn =~ /^cn=([^,]+)/i)
627   {
628     $dhcpcn = "$1";
629   }
630 $second = '' if not defined $second;
631 unless($second eq '' or $second =~ /^cn=[^,]+\s*,\s*\w+=[^,]+/i)
632   {
633     if($second =~ /^cn=[^,]+$/i)
634       {
635         # relative DN 'cn=name'
636         $second = "$second, $basedn";
637       }
638     elsif($second =~ /^\w+/)
639       {
640         # assume hostname only
641         $second = "cn=$second, $basedn";
642       }
643     else
644       {
645         usage(1, "invalid secondary '$second'")
646       }
647   }
648
649 usage(1) unless($ok);
650
651 if($i_conf ne "" and -f $i_conf)
652   {
653     if(not open(STDIN, '<', $i_conf))
654       {
655         print STDERR "Error: can't open conf file '$i_conf': $!\n";
656         exit(1);
657       }
658   }
659 if($o_ldif ne "")
660   {
661     if(-e $o_ldif)
662       {
663         print STDERR "Error: output ldif name '$o_ldif' already exists!\n";
664         exit(1);
665       }
666     if(not open(STDOUT, '>', $o_ldif))
667       {
668         print STDERR "Error: can't open ldif file '$o_ldif': $!\n";
669         exit(1);
670       }
671   }
672
673
674 print STDERR "Creating LDAP Configuration with the following options:\n";
675 print STDERR "\tBase DN: $basedn\n";
676 print STDERR "\tDHCP DN: $dhcpdn\n";
677 print STDERR "\tServer DN: cn=$server, $basedn\n";
678 print STDERR "\tSecondary DN: $second\n"
679              if(grep(/FaIlOvEr/i, @use) and $second ne '');
680 print STDERR "\n";
681
682 my $token;
683 my $token_number = 0;
684 my $line_number = 0;
685 my %curentry;
686 my $cursubnet = '';
687 my %curcounter = ( '' => { pool => 0, group => 0 } );
688
689 $current_dn = "$dhcpdn";
690 $curentry{'descr'} = $dhcpcn;
691 $line = '';
692 %failover = ();
693
694 while (($token = next_token (1)))
695   {
696     if ($token eq '}')
697       {
698         print_entry () if %curentry;
699         if($current_dn =~ /.+?,\s*${dhcpdn}$/) {
700           # don't go below dhcpdn ...
701           remove_dn_from_stack ();
702         }
703       }
704     elsif ($token eq 'subnet')
705       {
706         parse_subnet ();
707         next;
708       }
709     elsif ($token eq 'shared-network')
710       {
711         parse_shared_network ();
712         next;
713       }
714     elsif ($token eq 'class')
715       {
716         parse_class ();
717         next;
718       }
719     elsif ($token eq 'subclass')
720       {
721         parse_subclass ();
722         next;
723       }
724     elsif ($token eq 'pool')
725       {
726         parse_pool ();
727         next;
728       }
729     elsif ($token eq 'group')
730       {
731         parse_group ();
732         next;
733       }
734     elsif ($token eq 'host')
735       {
736         parse_host ();
737         next;
738       }
739     elsif ($token eq 'hardware')
740       {
741         parse_hwaddress ();
742         next;
743       }
744     elsif ($token eq 'range')
745       {
746         parse_range ();
747         next;
748       }
749     else
750       {
751         parse_statement ($token);
752         next;
753       }
754   }
755
756 close(STDIN)  if($i_conf);
757 close(STDOUT) if($o_ldif);
758
759 print STDERR "Done.\n";
760
This page took 0.133643 seconds and 3 git commands to generate.