#!/usr/bin/perl -I /root/tgmanage use strict; use Net::IP; use NetAddr::IP; BEGIN { require "include/config.pm"; eval { require "include/config.local.pm"; }; } my $base = "/etc"; $base = $ARGV[0] if $#ARGV > -1; $base .= "/" if not $base =~ m/\/$/ and not $base eq ""; my $dhcpd_base = $base . "dhcp/"; my $dhcpd_conf = $dhcpd_base . "dhcpd.conf"; my $dhcpd_pxeconf = $dhcpd_base . "v4-pxe-boot.conf"; my $dhcpd_wlc_conf = $dhcpd_base . "v4-wlc.conf"; my $dhcpd_voip_conf = $dhcpd_base . "v4-voip.conf"; my $dhcpd_fap_conf = $dhcpd_base . "v4-fap.conf"; # primary my $pri_range = Net::IP->new($nms::config::pri_net_v4) or die ("pri_range fail"); my $pri_mask = $pri_range->mask(); my $pri_net = $pri_range->ip(); # secondary my $sec_range = Net::IP->new($nms::config::sec_net_v4) or die ("sec_range fail"); my $sec_mask = $sec_range->mask(); my $sec_net = $sec_range->ip(); (my $sec_last = NetAddr::IP->new($nms::config::sec_net_v4)->last()) =~ s/\/[0-9]{1,2}//; (my $sec_gw = NetAddr::IP->new($nms::config::sec_net_v4)->first()) =~ s/\/[0-9]{1,2}//; my $sec_num = NetAddr::IP->new($nms::config::sec_net_v4)->num(); my $sec_n = $sec_num - int($sec_num / 4); # don't use more than 1/4 of the net for DHCP (my $sec_first = NetAddr::IP->new($nms::config::sec_net_v4)->nth($sec_n)) =~ s/\/[0-9]{1,2}//; # Create main configuration file for DHCP if ( not -f $dhcpd_conf ) { print STDERR "Creating file " . $dhcpd_conf . "\n"; open DHCPDFILE, ">" . $dhcpd_conf or die ( $! . " " . $dhcpd_conf); print DHCPDFILE <<"EOF"; # GENERATED BY make-dhcpd.pl # 60 min leasetime, 120 min max default-lease-time 3600; max-lease-time 7200; # make server authorative authoritative; # Don't let clients set their own FQDN ignore client-updates; # enable DDNS ddns-update-style interim; # set ddns-hostname if exists host-name { ddns-hostname = lcase(option host-name); } elsif exists fqdn.hostname { ddns-hostname = lcase(option fqdn.hostname); } else { ddns-hostname = binary-to-ascii(10, 8, "-", leased-address); } # set 'hardware' option to a variable # rebuilds the complete MAC in cases where you have a leading 0 set hostmac = concat ( suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":", suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":", suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":", suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":", suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":", suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2) ); # Domain name (unless overriden elsewhere) option domain-name "$nms::config::tgname.gathering.org"; option domain-name-servers $nms::config::pri_v4, $nms::config::sec_v4; key DHCP_UPDATER { algorithm HMAC-MD5.SIG-ALG.REG.INT; secret $nms::config::ddns_key; } # Servernetwork, Tele subnet $pri_net netmask $pri_mask {} # Servernetwork, NOC # Add small range at the end for PXE subnet $sec_net netmask $sec_mask { option subnet-mask $sec_mask; option routers $sec_gw; # No DDNS ddns-updates off; ddns-hostname = none; ddns-domainname = none; # supershort leasetime default-lease-time 300; max-lease-time 600; range $sec_first $sec_last; } include "/etc/dhcp/v4-revzones.conf"; include "/etc/dhcp/v4-generated-include.conf"; include "$dhcpd_pxeconf"; include "$dhcpd_wlc_conf"; include "$dhcpd_voip_conf"; include "$dhcpd_fap_conf"; EOF close DHCPDFILE; } # Create PXE-boot configuration file for DHCP if ( not -f $dhcpd_pxeconf ) { print STDERR "Creating file " . $dhcpd_pxeconf . "\n"; open PXEFILE, ">" . $dhcpd_pxeconf or die ( $! . " " . $dhcpd_pxeconf); print PXEFILE <<"EOF"; option arch code 93 = unsigned integer 16; if option arch = 00:07 { filename "bootx64.efi"; } else { filename "pxelinux.0"; } next-server $nms::config::pxe_server_v4; EOF close PXEFILE; } # Create WLC configuration file if ( not -f $dhcpd_wlc_conf ) { print STDERR "Creating file " . $dhcpd_wlc_conf . "\n"; open WLCFILE, ">" . $dhcpd_wlc_conf or die ( $! . " " . $dhcpd_wlc_conf); print WLCFILE <<"EOF"; option space WLC; option WLC.controller-address code 43 = text; class "access-points" { # Number of characters has to match the substring # I.e if "Access Point", you have to use (0, 12) match if substring (option vendor-class-identifier, 0, 12) = "Access Point"; if exists agent.circuit-id { log( info, concat( "AP: ", hostmac, " - ", option vendor-class-identifier, " - ", option agent.circuit-id )); } else { log( info, concat( "AP: ", hostmac, " - ", option vendor-class-identifier )); } vendor-option-space WLC; option WLC.controller-address "$nms::config::wlc1_v4"; } EOF close WLCFILE; } # Create VoIP config if ( not -f $dhcpd_voip_conf ) { print STDERR "Creating file " . $dhcpd_voip_conf . "\n"; open VOIPFILE, ">" . $dhcpd_voip_conf or die ( $! . " " . $dhcpd_voip_conf); print VOIPFILE <<"EOF"; option space CiscoVOIP; option CiscoVOIP.cm-tftp-server code 150 = array of ip-address; class "cisco-voip-lan" { match if substring (option vendor-class-identifier, 0, 28) = "Cisco Systems, Inc. IP Phone"; log( info, concat( "LOLOPHONE: " , option vendor-class-identifier )); vendor-option-space CiscoVOIP; option CiscoVOIP.cm-tftp-server $nms::config::voip1_v4; next-server $nms::config::voip1_v4; } class "cisco-voip-wlan" { match if substring (option vendor-class-identifier, 0, 33) = "Cisco Systems Inc. Wireless Phone"; log( info, concat( "BANANAPHONE: " , option vendor-class-identifier )); vendor-option-space CiscoVOIP; option CiscoVOIP.cm-tftp-server $nms::config::voip1_v4; next-server $nms::config::voip1_v4; } EOF close VOIPFILE; } # Create FAP/Gondul config if ( not -f $dhcpd_fap_conf ) { print STDERR "Creating file " . $dhcpd_fap_conf . "\n"; open FAPFILE, ">" . $dhcpd_fap_conf or die ( $! . " " . $dhcpd_fap_conf); print FAPFILE <<"EOF"; # FAP DHCP-configuration # Define structure of option 43 ( Zero Touch Protocol options) option space ztp; option ztp.image-file-name code 0 = text; option ztp.config-file-name code 1 = text; option ztp.image-file-type code 2 = text; option ztp.transfer-mode code 3 = text; option ztp.alt-image-file-name code 4 = text; # define option 150 - TFTP server (used for defining HTTP server for option 43) option option-150 code 150 = { ip-address }; # define option 60 - used for classifying ZTP clients ("vendor class identifier") option vendor-class-identifier code 60 = text; # only allow FAP "clients" class "fap-vendor-class" { # Vendor-Class Option 60, length 21: "Juniper-ex2200-48t-4g" # Vendor-Class Option 60, length 21: "Juniper-ex3300-48p" match if substring(option vendor-class-identifier, 0, 10) = "Juniper-ex"; log( info, concat( "FAP: ", hostmac, " (", option host-name, ") - ", option agent.circuit-id, " - ", option vendor-class-identifier )); } class "fap-mac" { # some Juniper switches won't send vendor-class-identifier match if ( ( binary-to-ascii(16, 8, ":", substring(hardware, 1, 3)) = "44:f4:77" ) or ( binary-to-ascii(16, 8, ":", substring(hardware, 1, 3)) = "f0:1c:2d" ) ); if not exists vendor-class-identifier { log( info, concat( "FAP: ", hostmac, " (", option host-name, ") - ", option agent.circuit-id )); } } group { # No DDNS ddns-updates off; ddns-hostname = none; ddns-domainname = none; # set short leasetime, so that it times out while the switch rebooting default-lease-time 120; max-lease-time 120; # ZTP Settings vendor-option-space ztp; option option-150 $nms::config::fap_server_v4; option tftp-server-name "$nms::config::fap_server_v4"; option ztp.transfer-mode "http"; option ztp.config-file-name = concat("api/config/", (option agent.circuit-id)); #option ztp.image-file-name "files/jinstall-ex-2200-14.1X53-D15.2-domestic-signed.tgz"; ### define ranges EOF foreach my $fap_net (@nms::config::fap_networks){ my $fap_subnet = Net::IP->new($fap_net)->ip(); my $fap_mask = Net::IP->new($fap_net)->mask(); (my $fap_last = NetAddr::IP->new($fap_net)->last()) =~ s/\/[0-9]{1,2}//; (my $fap_gw = NetAddr::IP->new($fap_net)->first()) =~ s/\/[0-9]{1,2}//; my $fap_num = NetAddr::IP->new($fap_net)->num(); my $fap_n = $fap_num - int($fap_num / 2); # don't use more than 1/2 of the net for DHCP (my $fap_first = NetAddr::IP->new($fap_net)->nth($fap_n)) =~ s/\/[0-9]{1,2}//; print FAPFILE <<"EOF"; subnet $fap_subnet netmask $fap_mask { option subnet-mask $fap_mask; option routers $fap_gw; pool { range $fap_first $fap_last; allow members of "fap-vendor-class"; allow members of "fap-mac"; } } EOF } print FAPFILE "}\n"; close FAPFILE; }