-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
MANGO-1817 - fix build methods in IpNetworkBuilder to get the Local IP Address, Broadcast Address and Subnet Mask #109
base: master
Are you sure you want to change the base?
Changes from 3 commits
1f0dafc
88711bf
f8afa56
83ac301
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ | |
|
||
import static com.serotonin.bacnet4j.npdu.ip.IpNetworkUtils.toIpAddrString; | ||
|
||
import org.apache.commons.lang3.StringUtils; | ||
import org.apache.commons.lang3.SystemUtils; | ||
|
||
import com.serotonin.bacnet4j.type.constructed.Address; | ||
import com.serotonin.bacnet4j.util.BACnetUtils; | ||
|
@@ -41,10 +41,12 @@ public class IpNetworkBuilder { | |
private String subnetMask; | ||
private int port = IpNetwork.DEFAULT_PORT; | ||
private int localNetworkNumber = Address.LOCAL_NETWORK; | ||
private boolean reuseAddress = false; | ||
final private boolean reuseAddress = (SystemUtils.IS_OS_WINDOWS) ? false : true; | ||
|
||
public IpNetworkBuilder withLocalBindAddress(final String localBindAddress) { | ||
this.localBindAddress = localBindAddress; | ||
this.broadcastAddress = IpNetworkUtils.getLocalBroadcastAddressString(localBindAddress); | ||
this.subnetMask = IpNetworkUtils.getSubnetMask(localBindAddress); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we are trying not to break existing usages of this code I think it would be wise to check if these fields are already set. if (this.broadcastAddress == null) {
this.broadcastAddress = IpNetworkUtils.getLocalBroadcastAddressString(localBindAddress);
}
if (this.subnetMask == null) {
this.subnetMask = IpNetworkUtils.getSubnetMask(localBindAddress);
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The operating systems would send the broadcast packets to the socket if and only if there is a socket opened for that specific broadcast address and that broadcast address & subnet mask is configured in the NIC. If the user happens to set it incorrectly, handling broadcast would become difficult. The same is applicable for local bind address. Hence it would be right approach if the BACnet4J is initialized with the right set of network parameters. As per the above comments there are chances that the network could be built with wrong set of parameters. Hence checking of null would not be advisable. Further to avoid any confusion to the end user, building the network with NIC name is created. This method is simple and error free. For example
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Correct, but we have always provided the opportunity to configure this wrong. I am simply requesting that we maintain existing behavior and no not override properties which were explicitly configured by the programmer. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This has still not been addressed. |
||
return this; | ||
} | ||
|
||
|
@@ -60,7 +62,7 @@ public IpNetworkBuilder withLocalBindAddress(final String localBindAddress) { | |
public IpNetworkBuilder withBroadcast(final String broadcastAddress, final int networkPrefixLength) { | ||
this.broadcastAddress = broadcastAddress; | ||
this.subnetMask = toIpAddrString(IpNetworkUtils.createMask(networkPrefixLength)); | ||
|
||
this.localBindAddress = IpNetworkUtils.getIPAddressString(this.broadcastAddress); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above said response |
||
return this; | ||
} | ||
|
||
|
@@ -83,7 +85,7 @@ public IpNetworkBuilder withSubnet(final String subnetAddress, final int network | |
final long subnet = IpNetworkUtils.bytesToLong(BACnetUtils.dottedStringToBytes(subnetAddress)); | ||
|
||
this.broadcastAddress = toIpAddrString(subnet | negMask); | ||
|
||
this.localBindAddress = IpNetworkUtils.getIPAddressString(this.broadcastAddress); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as above said response |
||
return this; | ||
} | ||
|
||
|
@@ -97,11 +99,13 @@ public IpNetworkBuilder withLocalNetworkNumber(final int localNetworkNumber) { | |
return this; | ||
} | ||
|
||
public IpNetworkBuilder withReuseAddress(final boolean reuseAddress) { | ||
this.reuseAddress = reuseAddress; | ||
public IpNetworkBuilder withInterfaceName(final String ifaceName ){ | ||
this.localBindAddress = IpNetworkUtils.getIPAddressString(ifaceName); | ||
this.broadcastAddress = IpNetworkUtils.getLocalBroadcastAddressString(ifaceName); | ||
this.subnetMask = IpNetworkUtils.getSubnetMask(ifaceName); | ||
return this; | ||
} | ||
|
||
public String getLocalBindAddress() { | ||
return localBindAddress; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -158,16 +158,60 @@ public static Address toAddress(final int networkNumber, final InetSocketAddress | |
return toAddress(networkNumber, addr.getAddress().getAddress(), addr.getPort()); | ||
} | ||
|
||
public static List<InterfaceAddress> getLocalInterfaceAddresses() { | ||
|
||
/** | ||
* Following details of the NIC (Network Interface Card) are stored in this record: | ||
* <ul> | ||
* <li> Integer format of index value of the NIC assigned by the OS </li> | ||
* <li> String format of the name of the NIC </li> | ||
* <li> String format of the IP4 Address assigned to the NIC </li> | ||
* <li> String format of the the broadcast Address of the NIC </li> | ||
* <li> String format of the SubnetMask of the NIC </li> | ||
* <li> Integer format of the Network Prefix Length of the NIC </li> | ||
* </ul> | ||
*/ | ||
public static record InterfaceInfo(int index, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using |
||
String interfaceName, | ||
String localIPAddress, | ||
String broadcastAddress, | ||
String subnetMask, | ||
int networkPrefixLength) {} | ||
|
||
|
||
/** | ||
* Convenient method to get the deails of Network Interface card | ||
* <p> | ||
* Values of the following parameters of each NIC are stored in record format: | ||
* <ul> | ||
* <li> index (int) - NIC Card Index assigned by the OS | ||
* <li> Name of the NIC (String) | ||
* <li> IP4 Address assigned to the NIC (String) | ||
* <li> Broadcast Address of the NIC (String) | ||
* <li> SubnetMask of the NIC calculated based on the NetworkPrefixLength (String) | ||
* <li> Network Prefix Length of the NIC (int) | ||
* </ul> | ||
* @return the list of records of all NIC available in this device | ||
*/ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A lot of these tags are not closed. Your IDE should be configured to warn about this. |
||
public static List<InterfaceInfo> getLocalInterfaceAddresses() { | ||
try { | ||
final List<InterfaceAddress> result = new ArrayList<>(); | ||
List<InterfaceInfo> ifaceDetails = new ArrayList<>(); | ||
for (final NetworkInterface iface : Collections.list(NetworkInterface.getNetworkInterfaces())) { | ||
for (final InterfaceAddress addr : iface.getInterfaceAddresses()) { | ||
if (!addr.getAddress().isLoopbackAddress() && addr.getAddress().isSiteLocalAddress()) | ||
result.add(addr); | ||
if (!addr.getAddress().isLoopbackAddress() && addr.getAddress().isSiteLocalAddress()){ | ||
String ifaceName = iface.getName(); | ||
String IPAddress = addr.getAddress().getHostName(); | ||
String broadcastAddress = addr.getBroadcast().getHostName(); | ||
final int networkPrefixLength = addr.getNetworkPrefixLength(); | ||
final long subnetMask = createMask(networkPrefixLength); | ||
String networkMask = toIpAddrString(subnetMask); | ||
int ifaceIndex = iface.getIndex(); | ||
InterfaceInfo ifaceInfo = new InterfaceInfo(ifaceIndex, ifaceName, IPAddress, broadcastAddress, networkMask, networkPrefixLength); | ||
ifaceDetails.add(ifaceInfo); | ||
} | ||
} | ||
|
||
} | ||
return result; | ||
return ifaceDetails; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning an immutable collection would be preferable. |
||
} catch (final Exception e) { | ||
// Should never happen, so just wrap in a RuntimeException | ||
throw new RuntimeException(e); | ||
|
@@ -199,4 +243,94 @@ public static String toIpAddrString(final long addr) { | |
sb.append(addr & 0xFF); | ||
return sb.toString(); | ||
} | ||
|
||
|
||
/** | ||
* Method to return the IP Address in dotted string based on | ||
* the broadcast Address string or IP Address string. | ||
* Throws exception if the Broadcast Address or IP address is | ||
* not configured in any of the available Network Interface | ||
* | ||
* @param host | ||
* @return the IP Address assigned to the NIC | ||
*/ | ||
public static String getIPAddressString(String host) | ||
{ | ||
try{ | ||
for (InterfaceInfo info:getLocalInterfaceAddresses()){ | ||
if (info.localIPAddress().equals(host) || | ||
info.interfaceName().equals(host) || | ||
info.broadcastAddress().equals(host)) | ||
return info.localIPAddress(); | ||
} | ||
throw new IllegalArgumentException(host + " is not configured in any interfaces."); | ||
|
||
|
||
}catch (final Exception e) { | ||
// Should never happen, so just wrap in a RuntimeException | ||
throw new RuntimeException(e); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe use an |
||
} | ||
} | ||
|
||
/** | ||
* Method to return the Broadcast IP Address configured | ||
* for the corresponding interface based on the | ||
* IP Address string or name of the NIC. | ||
* Throws exception if the IP address or interface name is | ||
* not configured in any of the available Network Interface. | ||
* | ||
* @param host | ||
* @return BroadcastAddress | ||
*/ | ||
public static String getLocalBroadcastAddressString(String host) | ||
{ | ||
try{ | ||
for (InterfaceInfo info:getLocalInterfaceAddresses()){ | ||
if (info.localIPAddress().equals(host) || | ||
(info.broadcastAddress() != null && | ||
info.broadcastAddress().equals(host)) || | ||
info.interfaceName().equals(host)) | ||
return info.broadcastAddress(); | ||
|
||
} | ||
|
||
throw new IllegalArgumentException(host + " is not configured in any interfaces."); | ||
|
||
}catch (final Exception e) { | ||
// Should never happen, so just wrap in a RuntimeException | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
|
||
|
||
/** | ||
* Method to return the the Subnet Mask configured | ||
* for the corresponding interface based on the | ||
* IP Address string or interface name. | ||
* Throws IllegalArguement exception if the IP address or | ||
* interface name is not configured in any of the | ||
* available Network Interface. | ||
* | ||
* @param host | ||
* @return SubnetMask of the NIC | ||
*/ | ||
public static String getSubnetMask(String host) | ||
{ | ||
try{ | ||
|
||
for (InterfaceInfo info:getLocalInterfaceAddresses()){ | ||
if (info.localIPAddress().equals(host) || | ||
info.interfaceName().equals(host) || | ||
info.broadcastAddress().equals(host)) | ||
return info.subnetMask(); | ||
} | ||
throw new IllegalArgumentException(host + " is not configured in any interfaces."); | ||
|
||
|
||
|
||
}catch (final Exception e) { | ||
// Should never happen, so just wrap in a RuntimeException | ||
throw new RuntimeException(e); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,10 @@ | |
|
||
package com.serotonin.bacnet4j.adhoc; | ||
|
||
|
||
import java.util.List; | ||
import java.util.Random; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
|
@@ -15,6 +19,7 @@ | |
import com.serotonin.bacnet4j.exception.BACnetException; | ||
import com.serotonin.bacnet4j.npdu.Network; | ||
import com.serotonin.bacnet4j.npdu.ip.IpNetworkBuilder; | ||
import com.serotonin.bacnet4j.npdu.ip.IpNetworkUtils; | ||
import com.serotonin.bacnet4j.service.unconfirmed.WhoIsRequest; | ||
import com.serotonin.bacnet4j.transport.DefaultTransport; | ||
import com.serotonin.bacnet4j.transport.Transport; | ||
|
@@ -34,12 +39,13 @@ public static void main(final String[] args) throws Exception { | |
} | ||
|
||
public LocalDevice createIpLocalDevice() throws Exception { | ||
Network network =network = new IpNetworkBuilder() | ||
.withLocalBindAddress("0.0.0.0") | ||
.withBroadcast("255.255.255.255", 24) | ||
Random rand = new Random(); | ||
List<IpNetworkUtils.InterfaceInfo> interfaceDetails = IpNetworkUtils.getLocalInterfaceAddresses(); | ||
final IpNetworkUtils.InterfaceInfo efaceInfo = interfaceDetails.get(rand.nextInt(interfaceDetails.size())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a random real network interface makes this test not repeatable. |
||
Network network = new IpNetworkBuilder() | ||
.withInterfaceName(efaceInfo.interfaceName()) | ||
.withPort(47808) | ||
.withLocalNetworkNumber(5) | ||
.withReuseAddress(true) | ||
.build(); | ||
|
||
Transport transport = new DefaultTransport(network); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,20 @@ | ||
package com.serotonin.bacnet4j.npdu.ip; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertThrows; | ||
import static org.junit.Assert.assertTrue; | ||
|
||
import java.util.List; | ||
import java.util.Random; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
public class IpNetworkBuilderTest { | ||
|
||
IpNetworkUtils.InterfaceInfo efaceInfo; | ||
List<IpNetworkUtils.InterfaceInfo> interfaceDetails; | ||
|
||
@Test | ||
public void withSubnet16() { | ||
final IpNetworkBuilder builder = new IpNetworkBuilder().withSubnet("192.168.0.0", 16); | ||
|
@@ -46,4 +56,61 @@ public void withBroadcast19() { | |
assertEquals("192.168.223.255", builder.getBroadcastAddress()); | ||
assertEquals("255.255.224.0", builder.getSubnetMask()); | ||
} | ||
|
||
@Before public void initialize(){ | ||
Random rand = new Random(); | ||
interfaceDetails = IpNetworkUtils.getLocalInterfaceAddresses(); | ||
efaceInfo = interfaceDetails.get(rand.nextInt(interfaceDetails.size())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using a random real network interface makes this test not repeatable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, lets not use the name |
||
} | ||
|
||
@Test | ||
public void withLocalIPAddress() throws RuntimeException{ | ||
|
||
String IPAddress = efaceInfo.localIPAddress(); | ||
final IpNetworkBuilder builder = new IpNetworkBuilder().withLocalBindAddress(IPAddress); | ||
assertEquals(IPAddress,builder.getLocalBindAddress()); | ||
assertEquals(efaceInfo.broadcastAddress(), builder.getBroadcastAddress()); | ||
assertEquals(efaceInfo.subnetMask(), builder.getSubnetMask()); | ||
} | ||
|
||
@Test | ||
public void withBroadcast() throws RuntimeException{ | ||
|
||
String IPAddress = efaceInfo.broadcastAddress(); | ||
final IpNetworkBuilder builder = new IpNetworkBuilder().withBroadcast(IPAddress, efaceInfo.networkPrefixLength()); | ||
assertEquals(IPAddress,builder.getBroadcastAddress()); | ||
assertEquals(efaceInfo.localIPAddress(), builder.getLocalBindAddress()); | ||
assertEquals(efaceInfo.subnetMask(), builder.getSubnetMask()); | ||
} | ||
|
||
|
||
@Test | ||
public void withLocalBindAddress_IncorrectIP() throws RuntimeException{ | ||
String IPAddress = "1.1.1.1"; | ||
Exception exception = assertThrows(RuntimeException.class,() -> { | ||
final IpNetworkBuilder builder = new IpNetworkBuilder().withLocalBindAddress(IPAddress);}); | ||
String actualMsg = exception.getMessage(); | ||
System.out.println(actualMsg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove System.out logging |
||
assertTrue((actualMsg.contains("IllegalArgument"))); | ||
} | ||
|
||
@Test | ||
public void verifyReuseAddress(){ | ||
final IpNetworkBuilder builder = new IpNetworkBuilder(); | ||
if (System.getProperty("os.name").contains("Windows")) | ||
assertTrue(!builder.isReuseAddress());//reuseAddress = false; | ||
else | ||
assertTrue(builder.isReuseAddress()); | ||
|
||
} | ||
|
||
@Test | ||
public void withIterfaceName() throws RuntimeException{ | ||
String ifaceName = efaceInfo.interfaceName(); | ||
final IpNetworkBuilder builder = new IpNetworkBuilder().withInterfaceName(ifaceName); | ||
assertEquals(efaceInfo.localIPAddress(), builder.getLocalBindAddress()); | ||
assertEquals(efaceInfo.broadcastAddress(),builder.getBroadcastAddress()); | ||
assertEquals(efaceInfo.subnetMask(), builder.getSubnetMask()); | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We definitely cannot target Java 21, since Mango only requires Java 17. We should discuss which version to target (I would be in favor of using Java 17).