/*
* Copyright 2009-2014 Jagornet Technologies, LLC. All Rights Reserved.
*
* This software is the proprietary information of Jagornet Technologies, LLC.
* Use is subject to license terms.
*
*/
/*
* This file JagornetDhcpServer.java is part of Jagornet DHCP.
*
* Jagornet DHCP is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Jagornet DHCP is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jagornet DHCP. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.jagornet.dhcp.server;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.jmx.HierarchyDynamicMBean;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.xmlbeans.XmlException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.jagornet.dhcp.Version;
import com.jagornet.dhcp.db.DbSchemaManager;
import com.jagornet.dhcp.db.IaManager;
import com.jagornet.dhcp.server.config.DhcpServerConfigException;
import com.jagornet.dhcp.server.config.DhcpServerConfiguration;
import com.jagornet.dhcp.server.config.DhcpServerPolicies;
import com.jagornet.dhcp.server.config.DhcpServerPolicies.Property;
import com.jagornet.dhcp.server.netty.NettyDhcpServer;
import com.jagornet.dhcp.server.request.binding.V4AddrBindingManager;
import com.jagornet.dhcp.server.request.binding.V6NaAddrBindingManager;
import com.jagornet.dhcp.server.request.binding.V6PrefixBindingManager;
import com.jagornet.dhcp.server.request.binding.V6TaAddrBindingManager;
import com.jagornet.dhcp.util.DhcpConstants;
import com.jagornet.dhcp.xml.DhcpServerConfigDocument.DhcpServerConfig;
/**
* The main DhcpV6Server class.
*/
public class JagornetDhcpServer
{
/** The log. */
private static Logger log = LoggerFactory.getLogger(JagornetDhcpServer.class);
/** The command line options. */
protected Options options;
/** The command line parser. */
protected CommandLineParser parser;
/** The help formatter. */
protected HelpFormatter formatter;
/** The default config filename. */
public static String DEFAULT_CONFIG_FILENAME = DhcpConstants.JAGORNET_DHCP_HOME != null ?
(DhcpConstants.JAGORNET_DHCP_HOME + "/conf/dhcpserver.xml") : "conf/dhcpserver.xml";
/** The configuration filename. */
protected String configFilename = DEFAULT_CONFIG_FILENAME;
/** The application context filename. */
public static String APP_CONTEXT_FILENAME = "com/jagornet/dhcp/context.xml";
public static String APP_CONTEXT_JDBC_DATASOURCE_FILENAME = "com/jagornet/dhcp/context_jdbc_datasource.xml";
public static String APP_CONTEXT_JDBC_DERBY_FILENAME = "com/jagornet/dhcp/context_jdbc_derby.xml";
public static String APP_CONTEXT_JDBC_H2_FILENAME = "com/jagornet/dhcp/context_jdbc_h2.xml";
public static String APP_CONTEXT_JDBC_SQLITE_FILENAME = "com/jagornet/dhcp/context_jdbc_sqlite.xml";
public static String APP_CONTEXT_JDBC_FILENAME = "com/jagornet/dhcp/context_jdbc.xml";
public static String APP_CONTEXT_JDBC_V1SCHEMA_FILENAME = "com/jagornet/dhcp/context_jdbc_v1schema.xml";
public static String APP_CONTEXT_JDBC_V2SCHEMA_FILENAME = "com/jagornet/dhcp/context_jdbc_v2schema.xml";
public static String APP_CONTEXT_SQLITE_V2SCHEMA_FILENAME = "com/jagornet/dhcp/context_sqlite_v2schema.xml";
public static String APP_CONTEXT_MONGO_V2SCHEMA_FILENAME = "com/jagornet/dhcp/context_mongo_v2schema.xml";
/** DHCPv6 Multicast interfaces */
protected List<NetworkInterface> v6McastNetIfs = null;
/** DHCPv6 Unicast addresses */
protected List<InetAddress> v6UcastAddrs = null;
/** DHCPv6 Server port number */
protected int v6PortNumber = DhcpConstants.V6_SERVER_PORT;
/** DHCPv4 Broadcast interface */
protected NetworkInterface v4BcastNetIf = null;
/** DHCPv4 Unicast addresses */
protected List<InetAddress> v4UcastAddrs = null;
/** DHCPv4 Server port number */
protected int v4PortNumber = DhcpConstants.V4_SERVER_PORT;
protected DhcpServerConfiguration serverConfig = null;
protected ApplicationContext context = null;
/**
* Instantiates the DHCPv6 server.
*
* @param args the command line argument array
*/
public JagornetDhcpServer(String[] args)
{
options = new Options();
parser = new BasicParser();
setupOptions();
if(!parseOptions(args)) {
System.err.println("Invalid command line options: " + Arrays.toString(args));
showHelp();
System.exit(0);
}
}
public void showHelp() {
formatter = new HelpFormatter();
String cliName = this.getClass().getName();
// formatter.printHelp(cliName, options);
PrintWriter stderr = new PrintWriter(System.err, true); // auto-flush=true
formatter.printHelp(stderr, 80, cliName + " [options]",
Version.getVersion(), options, 2, 2, null);
}
/**
* Start the DHCPv6 server. If multicast network interfaces have
* been supplied on startup, then start a NetDhcpServer thread
* on each of those interfaces. Start one NioDhcpServer thread
* which will listen on all IPv6 interfaces on the local host.
*
* @throws Exception the exception
*/
protected void start() throws Exception
{
log.info("Starting Jagornet DHCP Server");
log.info(Version.getVersion());
int cores = Runtime.getRuntime().availableProcessors();
log.info("Number of available core processors: " + cores);
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
log.info("Stopping Jagornet DHCPv6 Server");
System.out.println("Stopping Jagornet DHCPv6 Server: " + new Date());
}
});
DhcpServerConfiguration.configFilename = configFilename;
serverConfig = DhcpServerConfiguration.getInstance();
if (serverConfig == null) {
throw new IllegalStateException("Failed to initialize server configuration file: " +
configFilename);
}
String schemaType = DhcpServerPolicies.globalPolicy(Property.DATABASE_SCHEMA_TYTPE);
int schemaVersion = DhcpServerPolicies.globalPolicyAsInt(Property.DATABASE_SCHEMA_VERSION);
String[] appContext = getAppContextFiles(schemaType, schemaVersion);
log.info("Loading application context: " + Arrays.toString(appContext));
context = new ClassPathXmlApplicationContext(appContext);
if (context == null) {
throw new IllegalStateException("Failed to initialize application context: " +
appContext);
}
log.info("Application context loaded.");
loadManagers();
registerLog4jInJmx();
String msg = null;
// by default, all non-loopback, non-linklocal,
// IPv6 addresses are selected for unicast
if (v6UcastAddrs == null) {
v6UcastAddrs = getFilteredIPv6Addrs();
}
msg = "DHCPv6 Unicast addresses: " + Arrays.toString(v6UcastAddrs.toArray());
System.out.println(msg);
log.info(msg);
// for now, the mcast interfaces MUST be listed at
// startup to get the mcast behavior at all... but
// we COULD default to use all IPv6 interfaces
if (v6McastNetIfs != null) {
// msg = "DHCPv6 Multicast interfaces: " + Arrays.toString(mcastNetIfs.toArray());
StringBuilder sb = new StringBuilder();
sb.append("DHCPv6 Multicast interfaces: [");
for (NetworkInterface mcastNetIf : v6McastNetIfs) {
sb.append(mcastNetIf.getName());
sb.append(", ");
}
sb.setLength(sb.length()-2); // remove last ", "
sb.append(']');
msg = sb.toString();
System.out.println(msg);
log.info(msg);
}
else {
msg = "DHCPv6 Multicast interfaces: none";
System.out.println(msg);
log.info(msg);
}
msg = "DHCPv6 Port number: " + v6PortNumber;
System.out.println(msg);
log.info(msg);
// by default, all non-loopback, non-linklocal,
// IPv4 addresses are selected for unicast
if (v4UcastAddrs == null) {
v4UcastAddrs = getFilteredIPv4Addrs();
}
msg = "DHCPv4 Unicast addresses: " + Arrays.toString(v4UcastAddrs.toArray());
System.out.println(msg);
log.info(msg);
if (v4BcastNetIf != null) {
msg = "DHCPv4 Broadcast Interface: " + v4BcastNetIf.getName();
System.out.println(msg);
log.info(msg);
}
else {
msg = "DHCPv4 Broadcast interface: none";
System.out.println(msg);
log.info(msg);
}
msg = "DHCPv4 Port number: " + v4PortNumber;
System.out.println(msg);
log.info(msg);
NettyDhcpServer nettyServer = new NettyDhcpServer(v6UcastAddrs, v6McastNetIfs, v6PortNumber,
v4UcastAddrs, v4BcastNetIf, v4PortNumber);
nettyServer.start();
}
public static String[] getAppContextFiles(String schemaType, int schemaVersion) throws Exception {
List<String> appContexts = new ArrayList<String>();
String dbDir = DbSchemaManager.DB_HOME;
if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_DERBY) ||
schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_H2) ||
schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_SQLITE)) {
String jdbcContext = null;
if (schemaVersion == 1) {
jdbcContext = APP_CONTEXT_JDBC_V1SCHEMA_FILENAME;
}
else if (schemaVersion == 2) {
jdbcContext = APP_CONTEXT_JDBC_V2SCHEMA_FILENAME;
}
else {
throw new IllegalStateException("Unsupported schema version: " + schemaVersion);
}
if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_DERBY)) {
FileUtils.forceMkdir(new File(dbDir + "derby"));
appContexts.add(APP_CONTEXT_JDBC_DERBY_FILENAME);
appContexts.add(APP_CONTEXT_JDBC_DATASOURCE_FILENAME);
appContexts.add(jdbcContext);
appContexts.add(APP_CONTEXT_FILENAME);
}
else if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_H2)) {
FileUtils.forceMkdir(new File(dbDir + "h2"));
appContexts.add(APP_CONTEXT_JDBC_H2_FILENAME);
appContexts.add(APP_CONTEXT_JDBC_DATASOURCE_FILENAME);
appContexts.add(jdbcContext);
appContexts.add(APP_CONTEXT_FILENAME);
}
else if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_JDBC_SQLITE)) {
FileUtils.forceMkdir(new File(dbDir + "sqlite"));
appContexts.add(APP_CONTEXT_JDBC_SQLITE_FILENAME);
appContexts.add(APP_CONTEXT_JDBC_DATASOURCE_FILENAME);
appContexts.add(jdbcContext);
appContexts.add(APP_CONTEXT_FILENAME);
}
else {
log.warn("Unknown JDBC data source, using jdbc.properties");
appContexts.add(APP_CONTEXT_JDBC_FILENAME);
appContexts.add(APP_CONTEXT_JDBC_DATASOURCE_FILENAME);
appContexts.add(jdbcContext);
appContexts.add(APP_CONTEXT_FILENAME);
}
}
else if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_SQLITE)) {
FileUtils.forceMkdir(new File(dbDir + "sqlite"));
appContexts.add(APP_CONTEXT_SQLITE_V2SCHEMA_FILENAME);
appContexts.add(APP_CONTEXT_FILENAME);
}
else if (schemaType.equalsIgnoreCase(DbSchemaManager.SCHEMATYPE_MONGO)) {
appContexts.add(APP_CONTEXT_MONGO_V2SCHEMA_FILENAME);
appContexts.add(APP_CONTEXT_FILENAME);
}
else {
throw new DhcpServerConfigException("Unsupported schema type: " + schemaType);
}
String[] ctxArray = new String[appContexts.size()];
return appContexts.toArray(ctxArray);
}
private void loadManagers() throws Exception {
log.info("Loading managers from context...");
V6NaAddrBindingManager v6NaAddrBindingMgr =
(V6NaAddrBindingManager) context.getBean("v6NaAddrBindingManager");
if (v6NaAddrBindingMgr != null) {
try {
log.info("Initializing V6 NA Address Binding Manager");
v6NaAddrBindingMgr.init();
serverConfig.setNaAddrBindingMgr(v6NaAddrBindingMgr);
}
catch (Exception ex) {
log.error("Failed initialize V6 NA Address Binding Manager", ex);
throw ex;
}
}
else {
log.warn("No V6 NA Address Binding Manager available");
}
V6TaAddrBindingManager v6TaAddrBindingMgr =
(V6TaAddrBindingManager) context.getBean("v6TaAddrBindingManager");
if (v6TaAddrBindingMgr != null) {
try {
log.info("Initializing V6 TA Address Binding Manager");
v6TaAddrBindingMgr.init();
serverConfig.setTaAddrBindingMgr(v6TaAddrBindingMgr);
}
catch (Exception ex) {
log.error("Failed initialize V6 TA Address Binding Manager", ex);
throw ex;
}
}
else {
log.warn("No V6 TA Address Binding Manager available");
}
V6PrefixBindingManager v6PrefixBindingMgr =
(V6PrefixBindingManager) context.getBean("v6PrefixBindingManager");
if (v6PrefixBindingMgr != null) {
try {
log.info("Initializing V6 Prefix Binding Manager");
v6PrefixBindingMgr.init();
serverConfig.setPrefixBindingMgr(v6PrefixBindingMgr);
}
catch (Exception ex) {
log.error("Failed initialize V6 Prefix Binding Manager", ex);
throw ex;
}
}
else {
log.warn("No V6 Prefix Binding Manager available");
}
V4AddrBindingManager v4AddrBindingMgr =
(V4AddrBindingManager) context.getBean("v4AddrBindingManager");
if (v4AddrBindingMgr != null) {
try {
log.info("Initializing V4 Address Binding Manager");
v4AddrBindingMgr.init();
serverConfig.setV4AddrBindingMgr(v4AddrBindingMgr);
}
catch (Exception ex) {
log.error("Failed initialize V4 Address Binding Manager", ex);
throw ex;
}
}
else {
log.warn("No V4 Address Binding Manager available");
}
IaManager iaMgr = (IaManager) context.getBean("iaManager");
if (iaMgr != null) {
serverConfig.setIaMgr(iaMgr);
}
else {
log.warn("No IA Manager available");
}
log.info("Managers loaded.");
}
/**
* Setup command line options.
*/
@SuppressWarnings("static-access")
private void setupOptions()
{
Option configFileOption =
OptionBuilder.withLongOpt("configfile")
.withArgName("filename")
.withDescription("Configuration file (default = " + DEFAULT_CONFIG_FILENAME + ").")
.hasArg()
.create("c");
options.addOption(configFileOption);
Option portOption =
OptionBuilder.withLongOpt("v6port")
.withArgName("portnum")
.withDescription("DHCPv6 Port number (default = 547).")
.hasArg()
.create("6p");
options.addOption(portOption);
Option mcastOption =
OptionBuilder.withLongOpt("v6mcast")
.withArgName("interfaces")
.withDescription("DHCPv6 Multicast support (default = none). " +
"Use this option without arguments to instruct the server to bind to all " +
"multicast-enabled IPv6 interfaces on the host. Optionally, use arguments " +
"to list specific interfaces, separated by spaces.")
.hasOptionalArgs()
.create("6m");
options.addOption(mcastOption);
Option ucastOption =
OptionBuilder.withLongOpt("v6ucast")
.withArgName("addresses")
.withDescription("DHCPv6 Unicast addresses (default = all IPv6 addresses). " +
"Use this option to instruct the server to bind to a specific list " +
"of global IPv6 addresses, separated by spaces. These addresses " +
"should be configured on one or more DHCPv6 relay agents connected " +
"to DHCPv6 client links.")
.hasOptionalArgs()
.create("6u");
options.addOption(ucastOption);
Option v4BcastOption =
OptionBuilder.withLongOpt("v4bcast")
.withArgName("interface")
.withDescription("DHCPv4 broadcast support (default = none). " +
"Use this option to specify the interface for the server to " +
"receive and send broadcast DHCPv4 packets. Only one interface " +
"may be specified. All other interfaces on the host will only " +
"receive and send unicast traffic. The default IPv4 address on " +
"the specified interface will be used for determining the " +
"DHCPv4 client link within the server configuration file.")
.hasArg()
.create("4b");
options.addOption(v4BcastOption);
Option v4UcastOption =
OptionBuilder.withLongOpt("v4ucast")
.withArgName("addresses")
.withDescription("DHCPv4 Unicast addresses (default = all IPv4 addresses). " +
"Use this option to instruct the server to bind to a specific list " +
"of IPv4 addresses, separated by spaces. These addresses " +
"should be configured on one or more DHCPv4 relay agents connected " +
"to DHCPv4 client links.")
.hasOptionalArgs()
.create("4u");
options.addOption(v4UcastOption);
Option v4PortOption =
OptionBuilder.withLongOpt("v4port")
.withArgName("portnum")
.withDescription("DHCPv4 Port number (default = 67).")
.hasArg()
.create("4p");
options.addOption(v4PortOption);
Option testConfigFileOption =
OptionBuilder.withLongOpt("test-configfile")
.withArgName("filename")
.withDescription("Test configuration file, then exit.")
.hasArg()
.create("tc");
options.addOption(testConfigFileOption);
Option listIfOption = new Option("li", "list-interfaces", false,
"Show detailed host interface list, then exit.");
options.addOption(listIfOption);
Option versionOption = new Option("v", "version", false,
"Show version information, then exit.");
options.addOption(versionOption);
Option helpOption = new Option("?", "help", false, "Show this help page.");
options.addOption(helpOption);
}
/**
* Parses the command line options.
*
* @param args the command line argument array
*
* @return true, if all arguments were successfully parsed
*/
protected boolean parseOptions(String[] args)
{
try {
CommandLine cmd = parser.parse(options, args);
if (cmd.hasOption("?")) {
showHelp();
System.exit(0);
}
if (cmd.hasOption("c")) {
configFilename = cmd.getOptionValue("c");
}
if (cmd.hasOption("6p")) {
String p = cmd.getOptionValue("6p");
try {
v6PortNumber = Integer.parseInt(p);
}
catch (NumberFormatException ex) {
v6PortNumber = DhcpConstants.V6_SERVER_PORT;
System.err.println("Invalid port number: '" + p +
"' using default: " + v6PortNumber +
" Exception=" + ex);
}
}
if (cmd.hasOption("6m")) {
String[] ifnames = cmd.getOptionValues("6m");
if ((ifnames == null) || (ifnames.length < 1)) {
ifnames = new String[] { "*" };
}
v6McastNetIfs = getIPv6NetIfs(ifnames);
if ((v6McastNetIfs == null) || v6McastNetIfs.isEmpty()) {
return false;
}
}
if (cmd.hasOption("6u")) {
String[] addrs = cmd.getOptionValues("6u");
if ((addrs == null) || (addrs.length < 1)) {
addrs = new String[] { "*" };
}
v6UcastAddrs = getIpAddrs(addrs);
if ((v6UcastAddrs == null) || v6UcastAddrs.isEmpty()) {
return false;
}
}
if (cmd.hasOption("4b")) {
String v4if = cmd.getOptionValue("4b");
v4BcastNetIf = getIPv4NetIf(v4if);
if (v4BcastNetIf == null) {
return false;
}
}
if (cmd.hasOption("4u")) {
String[] addrs = cmd.getOptionValues("4u");
if ((addrs == null) || (addrs.length < 1)) {
addrs = new String[] { "*" };
}
v4UcastAddrs = getV4IpAddrs(addrs);
if ((v4UcastAddrs == null) || v4UcastAddrs.isEmpty()) {
return false;
}
}
if (cmd.hasOption("4p")) {
String p = cmd.getOptionValue("4p");
try {
v4PortNumber = Integer.parseInt(p);
}
catch (NumberFormatException ex) {
v4PortNumber = DhcpConstants.V4_SERVER_PORT;
System.err.println("Invalid port number: '" + p +
"' using default: " + v4PortNumber +
" Exception=" + ex);
}
}
if (cmd.hasOption("v")) {
System.err.println(Version.getVersion());
System.exit(0);
}
if (cmd.hasOption("tc")) {
try {
String filename = cmd.getOptionValue("tc");
System.err.println("Parsing server configuration file: " + filename);
DhcpServerConfig config = DhcpServerConfiguration.parseConfig(filename);
if (config != null) {
System.err.println("OK: " + filename + " is a valid DHCPv6 server configuration file.");
}
}
catch (Exception ex) {
System.err.println("ERROR: " + ex);
}
System.exit(0);
}
if (cmd.hasOption("li")) {
Enumeration<NetworkInterface> netIfs = NetworkInterface.getNetworkInterfaces();
if (netIfs != null) {
while (netIfs.hasMoreElements()) {
NetworkInterface ni = netIfs.nextElement();
System.err.println(ni);
}
}
System.exit(0);
}
}
catch (ParseException pe) {
System.err.println("Command line option parsing failure: " + pe);
return false;
} catch (SocketException se) {
System.err.println("Network interface socket failure: " + se);
return false;
} catch (UnknownHostException he) {
System.err.println("IP Address failure: " + he);
}
return true;
}
/**
* Gets the IPv6 network interfaces for the supplied interface names.
*
* @param ifnames the interface names to locate NetworkInterfaces by
*
* @return the list of NetworkInterfaces that are up, support multicast,
* and have at least one IPv6 address configured
*
* @throws SocketException the socket exception
*/
private List<NetworkInterface> getIPv6NetIfs(String[] ifnames) throws SocketException
{
List<NetworkInterface> netIfs = new ArrayList<NetworkInterface>();
for (String ifname : ifnames) {
if (ifname.equals("*")) {
return getAllIPv6NetIfs();
}
NetworkInterface netIf = NetworkInterface.getByName(ifname);
if (netIf == null) {
// if not found by name, see if the name is actually an address
try {
InetAddress ipaddr = InetAddress.getByName(ifname);
netIf = NetworkInterface.getByInetAddress(ipaddr);
}
catch (UnknownHostException ex) {
log.warn("Unknown interface: " + ifname + ": " + ex);
}
}
if (netIf != null) {
if (netIf.isUp()) {
// for multicast, the loopback interface is excluded
if (netIf.supportsMulticast() && !netIf.isLoopback()) {
boolean isV6 = false;
List<InterfaceAddress> ifAddrs =
netIf.getInterfaceAddresses();
for (InterfaceAddress ifAddr : ifAddrs) {
if (ifAddr.getAddress() instanceof Inet6Address) {
netIfs.add(netIf);
isV6 = true;
break;
}
}
if (!isV6) {
System.err.println("Interface is not configured for IPv6: " +
netIf);
return null;
}
}
else {
System.err.println("Interface does not support multicast: " +
netIf);
return null;
}
}
else {
System.err.println("Interface is not up: " +
netIf);
return null;
}
}
else {
System.err.println("Interface not found or inactive: " + ifname);
return null;
}
}
return netIfs;
}
/**
* Gets all IPv6 network interfaces on the local host.
*
* @return the list NetworkInterfaces
*/
private List<NetworkInterface> getAllIPv6NetIfs() throws SocketException
{
List<NetworkInterface> netIfs = new ArrayList<NetworkInterface>();
Enumeration<NetworkInterface> localInterfaces =
NetworkInterface.getNetworkInterfaces();
if (localInterfaces != null) {
while (localInterfaces.hasMoreElements()) {
NetworkInterface netIf = localInterfaces.nextElement();
// for multicast, the loopback interface is excluded
if (netIf.supportsMulticast() && !netIf.isLoopback()) {
Enumeration<InetAddress> ifAddrs = netIf.getInetAddresses();
while (ifAddrs.hasMoreElements()) {
InetAddress ip = ifAddrs.nextElement();
if (ip instanceof Inet6Address) {
netIfs.add(netIf);
break; // out to next interface
}
}
}
}
}
else {
log.error("No network interfaces found!");
}
return netIfs;
}
private List<InetAddress> getIpAddrs(String[] addrs) throws UnknownHostException
{
List<InetAddress> ipAddrs = new ArrayList<InetAddress>();
for (String addr : addrs) {
if (addr.equals("*")) {
return getAllIPv6Addrs();
}
InetAddress ipAddr = InetAddress.getByName(addr);
// allow only IPv6 addresses?
ipAddrs.add(ipAddr);
}
return ipAddrs;
}
static List<InetAddress> allIPv6Addrs;
public static List<InetAddress> getAllIPv6Addrs()
{
if (allIPv6Addrs == null) {
allIPv6Addrs = new ArrayList<InetAddress>();
try {
Enumeration<NetworkInterface> localInterfaces =
NetworkInterface.getNetworkInterfaces();
if (localInterfaces != null) {
while (localInterfaces.hasMoreElements()) {
NetworkInterface netIf = localInterfaces.nextElement();
Enumeration<InetAddress> ifAddrs = netIf.getInetAddresses();
while (ifAddrs.hasMoreElements()) {
InetAddress ip = ifAddrs.nextElement();
if (ip instanceof Inet6Address) {
allIPv6Addrs.add(ip);
}
}
}
}
else {
log.error("No network interfaces found!");
}
}
catch (IOException ex) {
log.error("Failed to get IPv6 addresses: " + ex);
}
}
return allIPv6Addrs;
}
public static List<InetAddress> getFilteredIPv6Addrs() {
boolean ignoreLoopback =
DhcpServerPolicies.globalPolicyAsBoolean(Property.DHCP_IGNORE_LOOPBACK);
boolean ignoreLinkLocal =
DhcpServerPolicies.globalPolicyAsBoolean(Property.DHCP_IGNORE_LINKLOCAL);
List<InetAddress> myV6Addrs = new ArrayList<InetAddress>();
List<InetAddress> allV6Addrs = getAllIPv6Addrs();
if (allV6Addrs != null) {
for (InetAddress ip : allV6Addrs) {
if (ignoreLoopback && ip.isLoopbackAddress()) {
log.debug("Skipping loopback address: " + ip);
continue;
}
if (ignoreLinkLocal && ip.isLinkLocalAddress()) {
log.debug("Skipping link local address: " + ip);
continue;
}
myV6Addrs.add(ip);
}
}
return myV6Addrs;
}
private NetworkInterface getIPv4NetIf(String ifname) throws SocketException
{
NetworkInterface netIf = NetworkInterface.getByName(ifname);
if (netIf == null) {
// if not found by name, see if the name is actually an address
try {
InetAddress ipaddr = InetAddress.getByName(ifname);
netIf = NetworkInterface.getByInetAddress(ipaddr);
}
catch (UnknownHostException ex) {
log.warn("Unknown interface: " + ifname + ": " + ex);
}
}
if (netIf != null) {
if (netIf.isUp()) {
// the loopback interface is excluded
if (!netIf.isLoopback()) {
boolean isV4 = false;
List<InterfaceAddress> ifAddrs =
netIf.getInterfaceAddresses();
for (InterfaceAddress ifAddr : ifAddrs) {
if (ifAddr.getAddress() instanceof Inet4Address) {
isV4 = true;
break;
}
}
if (!isV4) {
System.err.println("Interface is not configured for IPv4: " +
netIf);
return null;
}
}
else {
System.err.println("Interface is loopback: " +
netIf);
return null;
}
}
else {
System.err.println("Interface is not up: " +
netIf);
return null;
}
}
else {
System.err.println("Interface not found or inactive: " + ifname);
return null;
}
return netIf;
}
private List<InetAddress> getV4IpAddrs(String[] addrs) throws UnknownHostException
{
List<InetAddress> ipAddrs = new ArrayList<InetAddress>();
for (String addr : addrs) {
if (addr.equals("*")) {
return getAllIPv4Addrs();
}
InetAddress ipAddr = InetAddress.getByName(addr);
// allow only IPv4 addresses?
ipAddrs.add(ipAddr);
}
return ipAddrs;
}
static List<InetAddress> allIPv4Addrs;
public static List<InetAddress> getAllIPv4Addrs()
{
if (allIPv4Addrs == null) {
allIPv4Addrs = new ArrayList<InetAddress>();
try {
Enumeration<NetworkInterface> localInterfaces =
NetworkInterface.getNetworkInterfaces();
if (localInterfaces != null) {
while (localInterfaces.hasMoreElements()) {
NetworkInterface netIf = localInterfaces.nextElement();
Enumeration<InetAddress> ifAddrs = netIf.getInetAddresses();
while (ifAddrs.hasMoreElements()) {
InetAddress ip = ifAddrs.nextElement();
if (ip instanceof Inet4Address) {
allIPv4Addrs.add(ip);
}
}
}
}
else {
log.error("No network interfaces found!");
}
}
catch (IOException ex) {
log.error("Failed to get IPv4 addresses: " + ex);
}
}
return allIPv4Addrs;
}
public static List<InetAddress> getFilteredIPv4Addrs() {
boolean ignoreLoopback =
DhcpServerPolicies.globalPolicyAsBoolean(Property.DHCP_IGNORE_LOOPBACK);
boolean ignoreLinkLocal =
DhcpServerPolicies.globalPolicyAsBoolean(Property.DHCP_IGNORE_LINKLOCAL);
List<InetAddress> myV4Addrs = new ArrayList<InetAddress>();
List<InetAddress> allV4Addrs = getAllIPv4Addrs();
if (allV4Addrs != null) {
for (InetAddress ip : allV4Addrs) {
if (ignoreLoopback && ip.isLoopbackAddress()) {
log.debug("Skipping loopback address: " + ip);
continue;
}
if (ignoreLinkLocal && ip.isLinkLocalAddress()) {
log.debug("Skipping link local address: " + ip);
continue;
}
myV4Addrs.add(ip);
}
}
return myV4Addrs;
}
/**
* The main method.
*
* @param args the arguments
*/
public static void main(String[] args)
{
try {
JagornetDhcpServer server = new JagornetDhcpServer(args);
System.out.println("Starting Jagornet DHCP Server: " + new Date());
System.out.println(Version.getVersion());
server.start();
}
catch (Exception ex) {
System.err.println("DhcpServer ABORT!");
ex.printStackTrace();
System.exit(1);
}
}
/**
* Load server configuration. For use by the GUI.
*
* @param filename the configuration filename
*
* @return DhcpV6ServerConfig XML document object
*
* @throws DhcpServerConfigException, XmlException, IOException
*/
public static DhcpServerConfig loadConfig(String filename)
throws DhcpServerConfigException, XmlException, IOException
{
return DhcpServerConfiguration.loadConfig(filename);
}
/**
* Save server configuration. For use by the GUI.
*
* @param config DhcpV6ServerConfig XML document object
* @param filename the configuration filename
*
* @throws IOException the exception
*/
public static void saveConfig(DhcpServerConfig config, String filename) throws IOException
{
DhcpServerConfiguration.saveConfig(config, filename);
}
/**
* Static method to get the server configuration. For use by the GUI.
*
* @return the DhcpV6ServerConfig XML document object
*/
public static DhcpServerConfig getDhcpServerConfig()
{
return DhcpServerConfiguration.getInstance().getDhcpServerConfig();
}
/**
* Register Log4J in JMX to allow dynamic configuration
* of server logging using JMX client (e.g. jconsole).
*/
@SuppressWarnings("unchecked")
public static void registerLog4jInJmx()
{
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
try {
// Create and Register the top level Log4J MBean
HierarchyDynamicMBean hdm = new HierarchyDynamicMBean();
ObjectName mbo = new ObjectName("log4j:hiearchy=default");
mbs.registerMBean(hdm, mbo);
// Add the root logger to the Hierarchy MBean
org.apache.log4j.Logger rootLogger =
org.apache.log4j.Logger.getRootLogger();
hdm.addLoggerMBean(rootLogger.getName());
// Get each logger from the Log4J Repository and add it to
// the Hierarchy MBean created above.
LoggerRepository r = LogManager.getLoggerRepository();
Enumeration<Logger> loggers = r.getCurrentLoggers();
if (loggers != null) {
while (loggers.hasMoreElements()) {
org.apache.log4j.Logger logger =
(org.apache.log4j.Logger) loggers.nextElement();
hdm.addLoggerMBean(logger.getName());
}
}
}
catch (Exception ex) {
log.error("Failure registering Log4J in JMX: " + ex);
}
}
}