Skip to content
This repository has been archived by the owner on May 7, 2020. It is now read-only.

Commit

Permalink
make ip address usage configurable (#6005)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Bock <mbock@gmxpro.de>
  • Loading branch information
sobimibos authored and kaikreuzer committed Aug 15, 2018
1 parent 1b874a9 commit e6b3073
Show file tree
Hide file tree
Showing 5 changed files with 202 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,18 @@
<limitToOptions>true</limitToOptions>
<advanced>true</advanced>
</parameter>
<parameter name="useOnlyOneAddress" type="boolean">
<label>Single IP Address per Interface</label>
<description>Use only one IP address per interface and family</description>
<default>false</default>
<advanced>true</advanced>
</parameter>
<parameter name="useIPv6" type="boolean">
<label>Use IPv6</label>
<description>Use IPv6 Addresses if available</description>
<default>true</default>
<advanced>true</advanced>
</parameter>
</config-description>

</config-description:config-descriptions>
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public class NetUtil implements NetworkAddressService {
private static final String PRIMARY_ADDRESS = "primaryAddress";
private static final String BROADCAST_ADDRESS = "broadcastAddress";
private static final String POLL_INTERVAL = "pollInterval";
private static final String USE_ONLY_ONE_ADDRESS = "useOnlyOneAddress";
private static final String USE_IPV6 = "useIPv6";
private static final Logger LOGGER = LoggerFactory.getLogger(NetUtil.class);

/**
Expand All @@ -79,6 +81,8 @@ public class NetUtil implements NetworkAddressService {

private @Nullable String primaryAddress;
private @Nullable String configuredBroadcastAddress;
private boolean useOnlyOneAddress;
private boolean useIPv6;

// must be initialized before activate due to OSGi reference
private Set<NetworkAddressChangeListener> networkAddressChangeListeners = ConcurrentHashMap.newKeySet();
Expand Down Expand Up @@ -127,6 +131,9 @@ public synchronized void modified(Map<String, Object> config) {
configuredBroadcastAddress = broadcastAddressConf;
}

useOnlyOneAddress = getConfigParameter(config, USE_ONLY_ONE_ADDRESS, false);
useIPv6 = getConfigParameter(config, USE_IPV6, true);

Object pollIntervalSecondsObj = null;
int pollIntervalSeconds = POLL_INTERVAL_SECONDS;
try {
Expand Down Expand Up @@ -167,6 +174,29 @@ public synchronized void modified(Map<String, Object> config) {
return primaryIP;
}


/**
* Use only one address per interface and family (IPv4 and IPv6). If set listeners should bind only to one address
* per interface and family.
*
* @return use only one address per interface and family
*/
@Override
public boolean isUseOnlyOneAddress() {
return useOnlyOneAddress;
}

/**
* Use IPv6. If not set, IPv6 addresses should be completely ignored by listeners.
*
* @return use IPv6
*/
@Override
public boolean isUseIPv6() {
return useIPv6;
}


/**
* @deprecated Please use the NetworkAddressService with {@link #getPrimaryIpv4HostAddress()}
*
Expand Down Expand Up @@ -596,6 +626,24 @@ private void notifyPrimaryAddressChange(@Nullable String oldPrimaryAddress, @Nul
}
}

private boolean getConfigParameter(Map<String, Object> parameters, String parameter, boolean defaultValue) {
if (parameters == null) {
return defaultValue;
}
Object value = parameters.get(parameter);
if (value == null) {
return defaultValue;
}
if (value instanceof Boolean) {
return (Boolean) value;
}
if (value instanceof String) {
return Boolean.valueOf((String) value);
} else {
return defaultValue;
}
}

@Reference(policy = ReferencePolicy.DYNAMIC, cardinality = ReferenceCardinality.MULTIPLE)
protected void addNetworkAddressChangeListener(NetworkAddressChangeListener listener) {
networkAddressChangeListeners.add(listener);
Expand All @@ -613,5 +661,4 @@ protected void setSafeCaller(SafeCaller safeCaller) {
protected void unsetSafeCaller(SafeCaller safeCaller) {
this.safeCaller = null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,19 @@ public interface NetworkAddressService {
*/
@Nullable
String getConfiguredBroadcastAddress();

/**
* Use only one address per interface and family (IPv4 and IPv6). If set listeners should bind only to one address
* per interface and family.
*
* @return use only one address per interface and family
*/
boolean isUseOnlyOneAddress();

/**
* Use IPv6. If not set, IPv6 addresses should be completely ignored by listeners.
*
* @return use IPv6
*/
boolean isUseIPv6();
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,43 @@ public ServiceDescription(String serviceType, String serviceName, int servicePor
this.serviceProperties = serviceProperties;
}

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((serviceName == null) ? 0 : serviceName.hashCode());
result = prime * result + servicePort;
result = prime * result + ((serviceType == null) ? 0 : serviceType.hashCode());
return result;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ServiceDescription other = (ServiceDescription) obj;
if (serviceName == null) {
if (other.serviceName != null)
return false;
} else if (!serviceName.equals(other.serviceName))
return false;
if (servicePort != other.servicePort)
return false;
if (serviceType == null) {
if (other.serviceType != null)
return false;
} else if (!serviceType.equals(other.serviceType))
return false;
return true;
}

@Override
public String toString() {
return "ServiceDescription [serviceType=" + serviceType + ", serviceName=" + serviceName + ", servicePort="
+ servicePort + "]";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,37 @@
package org.eclipse.smarthome.io.transport.mdns.internal;

import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.time.Duration;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentSkipListSet;

import javax.jmdns.JmDNS;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;

import org.eclipse.smarthome.core.net.CidrAddress;
import org.eclipse.smarthome.core.net.NetworkAddressChangeListener;
import org.eclipse.smarthome.core.net.NetworkAddressService;
import org.eclipse.smarthome.io.transport.mdns.MDNSClient;
import org.eclipse.smarthome.io.transport.mdns.ServiceDescription;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -51,7 +60,11 @@ public class MDNSClientImpl implements MDNSClient, NetworkAddressChangeListener

private final ConcurrentMap<InetAddress, JmDNS> jmdnsInstances = new ConcurrentHashMap<>();

private static Set<InetAddress> getAllInetAddresses() {
private final ConcurrentSkipListSet<ServiceDescription> activeServices = new ConcurrentSkipListSet<>();

private NetworkAddressService networkAddressService;

private Set<InetAddress> getAllInetAddresses() {
final Set<InetAddress> addresses = new HashSet<>();
Enumeration<NetworkInterface> itInterfaces;
try {
Expand All @@ -62,19 +75,57 @@ private static Set<InetAddress> getAllInetAddresses() {
while (itInterfaces.hasMoreElements()) {
final NetworkInterface iface = itInterfaces.nextElement();
try {
if (!iface.isUp() || iface.isLoopback()) {
if (!iface.isUp() || iface.isLoopback() || iface.isPointToPoint()) {
continue;
}
} catch (final SocketException ex) {
continue;
}

InetAddress primaryIPv4HostAddress = null;

if (networkAddressService.isUseOnlyOneAddress()
&& networkAddressService.getPrimaryIpv4HostAddress() != null) {
final Enumeration<InetAddress> itAddresses = iface.getInetAddresses();
while (itAddresses.hasMoreElements()) {
final InetAddress address = itAddresses.nextElement();
if (address.getHostAddress().equals(networkAddressService.getPrimaryIpv4HostAddress())) {
primaryIPv4HostAddress = address;
break;
}
}
}

final Enumeration<InetAddress> itAddresses = iface.getInetAddresses();
boolean ipv4addressAdded = false;
boolean ipv6addressAdded = false;
while (itAddresses.hasMoreElements()) {
final InetAddress address = itAddresses.nextElement();
if (address.isLoopbackAddress() || address.isLinkLocalAddress()) {
if (address.isLoopbackAddress() || address.isLinkLocalAddress()
|| (!networkAddressService.isUseIPv6() && address instanceof Inet6Address)) {
continue;
}
addresses.add(address);
if (networkAddressService.isUseOnlyOneAddress()) {
// add only one address per interface and family
if (address instanceof Inet4Address) {
if (!ipv4addressAdded) {
if (primaryIPv4HostAddress != null) {
// use configured primary address instead of first one
addresses.add(primaryIPv4HostAddress);
} else {
addresses.add(address);
}
ipv4addressAdded = true;
}
} else if (address instanceof Inet6Address) {
if (!ipv6addressAdded) {
addresses.add(address);
ipv6addressAdded = true;
}
}
} else {
addresses.add(address);
}
}
}
return addresses;
Expand All @@ -86,15 +137,27 @@ public Set<JmDNS> getClientInstances() {
}

@Activate
public void activate() {
protected void activate() {
start();
}

private void start() {
for (InetAddress address : getAllInetAddresses()) {
createJmDNSByAddress(address);
}
for (ServiceDescription description : activeServices) {
try {
registerServiceInternal(description);
} catch (IOException e) {
logger.warn("Exception while registering service {}", description, e);
}
}
}

@Deactivate
public void deactivate() {
close();
activeServices.clear();
}

@Override
Expand All @@ -109,6 +172,11 @@ public void removeServiceListener(String type, ServiceListener listener) {

@Override
public void registerService(ServiceDescription description) throws IOException {
activeServices.add(description);
registerServiceInternal(description);
}

private void registerServiceInternal(ServiceDescription description) throws IOException {
for (JmDNS instance : jmdnsInstances.values()) {
logger.debug("Registering new service {} at {}:{} ({})", description.serviceType,
instance.getInetAddress().getHostAddress(), description.servicePort, instance.getName());
Expand All @@ -119,8 +187,10 @@ public void registerService(ServiceDescription description) throws IOException {
}
}


@Override
public void unregisterService(ServiceDescription description) {
activeServices.remove(description);
for (JmDNS instance : jmdnsInstances.values()) {
try {
logger.debug("Unregistering service {} at {}:{} ({})", description.serviceType,
Expand All @@ -136,6 +206,7 @@ public void unregisterService(ServiceDescription description) {

@Override
public void unregisterAllServices() {
activeServices.clear();
for (JmDNS instance : jmdnsInstances.values()) {
instance.unregisterAllServices();
}
Expand Down Expand Up @@ -163,7 +234,9 @@ public ServiceInfo[] list(String type, Duration timeout) {
public void close() {
for (JmDNS jmdns : jmdnsInstances.values()) {
closeQuietly(jmdns);
logger.debug("mDNS service has been stopped ({})", jmdns.getName());
}
jmdnsInstances.clear();
}

private void closeQuietly(JmDNS jmdns) {
Expand Down Expand Up @@ -203,27 +276,17 @@ private void createJmDNSByAddress(InetAddress address) {

@Override
public void onChanged(List<CidrAddress> added, List<CidrAddress> removed) {
// remove jmdns instances that no longer exist due to interface changed
for (CidrAddress cidrAddress : removed) {
InetAddress inetAddr = cidrAddress.getAddress();
if (jmdnsInstances.containsKey(inetAddr)) {
JmDNS jmdns = jmdnsInstances.get(inetAddr);
closeQuietly(jmdns);
jmdnsInstances.remove(inetAddr);
logger.debug("mDNS service has been removed ({} for IP {})", jmdns.getName(),
inetAddr.getHostAddress());
}
}
logger.debug("ip address change: added {}, removed {}", added, removed);
close();
start();
}

// add the new addresses, just like activate
for (CidrAddress cidrAddress : added) {
InetAddress address = cidrAddress.getAddress();
@Reference
protected void setNetworkAddressService(NetworkAddressService networkAddressService) {
this.networkAddressService = networkAddressService;
}

// skip the loopback or link local addresses
if (address.isLoopbackAddress() || address.isLinkLocalAddress()) {
continue;
}
createJmDNSByAddress(address);
}
protected void unsetNetworkAddressService(NetworkAddressService networkAddressService) {
this.networkAddressService = null;
}
}

0 comments on commit e6b3073

Please sign in to comment.