From 678adf2ca80a0bbbff9953e9372695184e415aac Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Thu, 6 Jun 2013 14:06:27 -0700 Subject: [PATCH] Adding Set Next Hop action - It will be ignored by openflow plugin Signed-off-by: Alessandro Boch --- .../forwardingrulesmanager/FlowConfig.java | 240 ++++++++---------- .../forwardingrulesmanager/frmTest.java | 15 -- .../openflow/internal/FlowConverter.java | 22 +- .../controller/sal/action/SetNextHop.java | 64 +++++ .../controller/sal/utils/NetUtils.java | 48 ++-- .../controller/sal/action/ActionTest.java | 27 +- .../controller/sal/utils/NetUtilsTest.java | 15 +- 7 files changed, 245 insertions(+), 186 deletions(-) create mode 100644 opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/action/SetNextHop.java diff --git a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java index f3429e62b1..77c1c859c6 100644 --- a/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java +++ b/opendaylight/forwardingrulesmanager/api/src/main/java/org/opendaylight/controller/forwardingrulesmanager/FlowConfig.java @@ -33,6 +33,7 @@ import org.opendaylight.controller.sal.action.Output; import org.opendaylight.controller.sal.action.PopVlan; import org.opendaylight.controller.sal.action.SetDlDst; import org.opendaylight.controller.sal.action.SetDlSrc; +import org.opendaylight.controller.sal.action.SetNextHop; import org.opendaylight.controller.sal.action.SetNwDst; import org.opendaylight.controller.sal.action.SetNwSrc; import org.opendaylight.controller.sal.action.SetNwTos; @@ -124,27 +125,6 @@ public class FlowConfig implements Serializable { ANY, V4, V6; }; - private enum SetNextHopType { - CISCO_EXTENSION("Cisco NextHop Extension"), RESOLVE_L2RW( - "Resolve L2 Rewrite"); - - private SetNextHopType(String name) { - this.name = name; - } - - private String name; - - public String toString() { - return name; - } - - public boolean equals(String type) { - if (type.trim().equalsIgnoreCase(name)) - return true; - return false; - } - } - public FlowConfig() { } @@ -473,163 +453,160 @@ public class FlowConfig implements Serializable { @Override public boolean equals(Object obj) { - if (this == obj) + if (this == obj) { return true; - if (obj == null) + } + if (obj == null) { return false; - if (getClass() != obj.getClass()) + } + if (getClass() != obj.getClass()) { return false; + } FlowConfig other = (FlowConfig) obj; if (actions == null) { - if (other.actions != null) + if (other.actions != null) { return false; - } else if (!actions.equals(other.actions)) + } + } else if (!actions.equals(other.actions)) { return false; + } if (cookie == null) { - if (other.cookie != null) + if (other.cookie != null) { return false; - } else if (!cookie.equals(other.cookie)) + } + } else if (!cookie.equals(other.cookie)) { return false; + } if (dlDst == null) { - if (other.dlDst != null) + if (other.dlDst != null) { return false; - } else if (!dlDst.equals(other.dlDst)) + } + } else if (!dlDst.equals(other.dlDst)) { return false; + } if (dlSrc == null) { - if (other.dlSrc != null) + if (other.dlSrc != null) { return false; - } else if (!dlSrc.equals(other.dlSrc)) + } + } else if (!dlSrc.equals(other.dlSrc)) { return false; - if (dynamic != other.dynamic) + } + if (dynamic != other.dynamic) { return false; + } if (etherType == null) { - if (other.etherType != null) + if (other.etherType != null) { return false; - } else if (!etherType.equals(other.etherType)) + } + } else if (!etherType.equals(other.etherType)) { return false; + } if (ingressPort == null) { - if (other.ingressPort != null) + if (other.ingressPort != null) { return false; - } else if (!ingressPort.equals(other.ingressPort)) + } + } else if (!ingressPort.equals(other.ingressPort)) { return false; + } if (name == null) { - if (other.name != null) + if (other.name != null) { return false; - } else if (!name.equals(other.name)) + } + } else if (!name.equals(other.name)) { return false; + } if (nwDst == null) { - if (other.nwDst != null) + if (other.nwDst != null) { return false; - } else if (!nwDst.equals(other.nwDst)) + } + } else if (!nwDst.equals(other.nwDst)) { return false; + } if (nwSrc == null) { - if (other.nwSrc != null) + if (other.nwSrc != null) { return false; - } else if (!nwSrc.equals(other.nwSrc)) + } + } else if (!nwSrc.equals(other.nwSrc)) { return false; + } if (portGroup == null) { - if (other.portGroup != null) + if (other.portGroup != null) { return false; - } else if (!portGroup.equals(other.portGroup)) + } + } else if (!portGroup.equals(other.portGroup)) { return false; + } if (priority == null) { - if (other.priority != null) + if (other.priority != null) { return false; - } else if (!priority.equals(other.priority)) + } + } else if (!priority.equals(other.priority)) { return false; + } if (protocol == null) { - if (other.protocol != null) + if (other.protocol != null) { return false; - } else if (!protocol.equals(other.protocol)) + } + } else if (!protocol.equals(other.protocol)) { return false; + } if (node == null) { - if (other.node != null) + if (other.node != null) { return false; - } else if (!node.equals(other.node)) + } + } else if (!node.equals(other.node)) { return false; + } if (tosBits == null) { - if (other.tosBits != null) + if (other.tosBits != null) { return false; - } else if (!tosBits.equals(other.tosBits)) + } + } else if (!tosBits.equals(other.tosBits)) { return false; + } if (tpDst == null) { - if (other.tpDst != null) + if (other.tpDst != null) { return false; - } else if (!tpDst.equals(other.tpDst)) + } + } else if (!tpDst.equals(other.tpDst)) { return false; + } if (tpSrc == null) { - if (other.tpSrc != null) + if (other.tpSrc != null) { return false; - } else if (!tpSrc.equals(other.tpSrc)) + } + } else if (!tpSrc.equals(other.tpSrc)) { return false; + } if (vlanId == null) { - if (other.vlanId != null) + if (other.vlanId != null) { return false; - } else if (!vlanId.equals(other.vlanId)) + } + } else if (!vlanId.equals(other.vlanId)) { return false; + } if (vlanPriority == null) { - if (other.vlanPriority != null) + if (other.vlanPriority != null) { return false; - } else if (!vlanPriority.equals(other.vlanPriority)) + } + } else if (!vlanPriority.equals(other.vlanPriority)) { return false; + } if (idleTimeout == null) { - if (other.idleTimeout != null) + if (other.idleTimeout != null) { return false; - } else if (!idleTimeout.equals(other.idleTimeout)) + } + } else if (!idleTimeout.equals(other.idleTimeout)) { return false; + } if (hardTimeout == null) { - if (other.hardTimeout != null) + if (other.hardTimeout != null) { return false; - } else if (!hardTimeout.equals(other.hardTimeout)) - return false; - return true; - } - - public InetAddress getNextHopAddressForL2RWAction() { - if (actions != null) { - Matcher sstr; - for (String actiongrp : actions) { - sstr = Pattern.compile("SET_NEXT_HOP=(.*)").matcher(actiongrp); - if (sstr.matches()) { - SetNextHopType setNHType = SetNextHopType.CISCO_EXTENSION; - String nextHopInfo = sstr.group(1); - String values[] = nextHopInfo.split("//"); - String address = values[0].trim(); - String type = null; - if (values.length > 1) { - type = values[1].trim(); - } - - if (type != null) { - for (SetNextHopType nh : SetNextHopType.values()) { - if (nh.equals(type)) - setNHType = nh; - } - } - - log.debug("Get Nexthop address = {} Type = {}", address, - setNHType.toString()); - if (setNHType == SetNextHopType.RESOLVE_L2RW) { - try { - return InetAddress.getByName(address); - } catch (Exception e) { - log.debug( - "Exception during nextHopAddress resolution : ", - e); - } - } - } } + } else if (!hardTimeout.equals(other.hardTimeout)) { + return false; } - return null; - } - - public String getNextHopL2RWUsageError() { - return "Could not resolve NextHop IP Address for the selected Switch.
" - + "Please Check the following configurations.
" - + "1. Is the NextHop IP address directly connected to the Selected Switch
" - + "2. If appropriate Subnet Configurations are done in the Switch Manager
" - + "3. If the Nexthop IP-Address is Correct"; + return true; } public boolean isL2AddressValid(String mac) { @@ -772,8 +749,9 @@ public class FlowConfig implements Serializable { } // make sure it's a valid number - if (cookie != null) + if (cookie != null) { Long.decode(cookie); + } if (ingressPort != null) { Short port = Short.decode(ingressPort); @@ -812,10 +790,11 @@ public class FlowConfig implements Serializable { "Ethernet type %s is not valid", etherType)); return false; } else { - if (type == 0x800) + if (type == 0x800) { etype = EtherIPType.V4; - else if (type == 0x86dd) + } else if (type == 0x86dd) { etype = EtherIPType.V6; + } } } @@ -1084,13 +1063,10 @@ public class FlowConfig implements Serializable { ActionType.SET_NEXT_HOP.toString() + "=(.*)") .matcher(actiongrp); if (sstr.matches()) { - String nextHopInfo = sstr.group(1); - String values[] = nextHopInfo.split("//"); - String address = values[0].trim(); - - if ((address == null) || !isOutputNextHopValid(address)) { + if (!NetUtils.isIPAddressValid(sstr.group(1))) { resultStr.append(String.format( - "next hop %s is not valid", sstr.group(1))); + "IP destination address %s is not valid", + sstr.group(1))); return false; } continue; @@ -1198,17 +1174,6 @@ public class FlowConfig implements Serializable { return flow; } - public boolean isOutputNextHopValid(String onh) { - if (onh == null) { - return false; - } - /* - * For now, only takes IPv4 or IPv6 address - */ - return (NetUtils.isIPv4AddressValid(onh) || NetUtils - .isIPv6AddressValid(onh)); - } - public boolean isByNameAndNodeIdEqual(FlowConfig that) { return (this.name.equals(that.name) && this.node.equals(that.node)); } @@ -1221,14 +1186,6 @@ public class FlowConfig implements Serializable { return this.node.equals(node); } - public static List getSupportedNextHopTypes() { - List s = new ArrayList(); - for (SetNextHopType nh : SetNextHopType.values()) { - s.add(nh.toString()); - } - return s; - } - public void toggleStatus() { installInHw = (installInHw == null) ? "true" : (installInHw .equals("true")) ? "false" : "true"; @@ -1393,7 +1350,8 @@ public class FlowConfig implements Serializable { ActionType.SET_NEXT_HOP.toString() + "=(.*)").matcher( actiongrp); if (sstr.matches()) { - log.warn("We do not handle next hop action yet...."); + actionList.add(new SetNextHop(NetUtils.parseInetAddress(sstr + .group(1)))); continue; } } diff --git a/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java b/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java index 0b11d13f3c..34337ba42b 100644 --- a/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java +++ b/opendaylight/forwardingrulesmanager/api/src/test/java/org/opendaylight/controller/forwardingrulesmanager/frmTest.java @@ -339,21 +339,6 @@ public class frmTest { } - @Test - public void testFlowConfigNextHopValidity() throws UnknownHostException { - FlowConfig fc = new FlowConfig(); - Assert.assertFalse(fc.isOutputNextHopValid(null)); - Assert.assertFalse(fc.isOutputNextHopValid("abc")); - Assert.assertFalse(fc.isOutputNextHopValid("1.1.1")); - Assert.assertFalse(fc.isOutputNextHopValid("1.1.1.1/49")); - - Assert.assertTrue(fc.isOutputNextHopValid("1.1.1.1")); - Assert.assertTrue(fc.isOutputNextHopValid("1.1.1.1/32")); - Assert.assertTrue(fc - .isOutputNextHopValid("2001:420:281:1004:407a:57f4:4d15:c355")); - - } - @Test public void testFlowConfigEqualities() throws UnknownHostException { FlowConfig fc = new FlowConfig(); diff --git a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/FlowConverter.java b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/FlowConverter.java index 074774a25e..8d3a1be203 100644 --- a/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/FlowConverter.java +++ b/opendaylight/protocol_plugins/openflow/src/main/java/org/opendaylight/controller/protocol_plugin/openflow/internal/FlowConverter.java @@ -100,7 +100,7 @@ public class FlowConverter { /** * Returns the match in OF 1.0 (OFMatch) form or OF 1.0 + IPv6 extensions * form (V6Match) - * + * * @return */ public OFMatch getOFMatch() { @@ -177,7 +177,7 @@ public class FlowConverter { * actually the DSCP field followed by a zero ECN */ byte tos = (Byte) match.getField(MatchType.NW_TOS).getValue(); - byte dscp = (byte) ((int) tos << 2); + byte dscp = (byte) (tos << 2); if (!isIPv6) { ofMatch.setNetworkTypeOfService(dscp); wildcards &= ~OFMatch.OFPFW_NW_TOS; @@ -262,7 +262,7 @@ public class FlowConverter { /** * Returns the list of actions in OF 1.0 form - * + * * @return */ public List getOFActions() { @@ -411,7 +411,7 @@ public class FlowConverter { continue; } if (action.getType() == ActionType.SET_NEXT_HOP) { - // TODO + logger.info("Unsupported action: {}", action); continue; } } @@ -424,7 +424,7 @@ public class FlowConverter { /** * Utility to convert a SAL flow to an OF 1.0 (OFFlowMod) or to an OF 1.0 + * IPv6 extension (V6FlowMod) Flow modifier Message - * + * * @param sw * @param command * @param port @@ -505,7 +505,7 @@ public class FlowConverter { if (ofMatch.getInputPort() != 0) { salMatch.setField(new MatchField(MatchType.IN_PORT, NodeConnectorCreator.createNodeConnector( - (Short) ofMatch.getInputPort(), node))); + ofMatch.getInputPort(), node))); } if (ofMatch.getDataLayerSource() != null && !NetUtils @@ -560,11 +560,11 @@ public class FlowConverter { } if (ofMatch.getTransportSource() != 0) { salMatch.setField(MatchType.TP_SRC, - ((Short) ofMatch.getTransportSource())); + ofMatch.getTransportSource()); } if (ofMatch.getTransportDestination() != 0) { salMatch.setField(MatchType.TP_DST, - ((Short) ofMatch.getTransportDestination())); + ofMatch.getTransportDestination()); } } else { // Compute OF1.0 + IPv6 extensions Match @@ -573,7 +573,7 @@ public class FlowConverter { // Mask on input port is not defined salMatch.setField(new MatchField(MatchType.IN_PORT, NodeConnectorCreator.createOFNodeConnector( - (Short) v6Match.getInputPort(), node))); + v6Match.getInputPort(), node))); } if (v6Match.getDataLayerSource() != null && !NetUtils @@ -623,11 +623,11 @@ public class FlowConverter { } if (v6Match.getTransportSource() != 0) { salMatch.setField(MatchType.TP_SRC, - ((Short) v6Match.getTransportSource())); + (v6Match.getTransportSource())); } if (v6Match.getTransportDestination() != 0) { salMatch.setField(MatchType.TP_DST, - ((Short) v6Match.getTransportDestination())); + (v6Match.getTransportDestination())); } } } diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/action/SetNextHop.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/action/SetNextHop.java new file mode 100644 index 0000000000..33d9b9361d --- /dev/null +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/action/SetNextHop.java @@ -0,0 +1,64 @@ +package org.opendaylight.controller.sal.action; + +import java.net.InetAddress; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.NONE) +public class SetNextHop extends Action { + @XmlElement + private InetAddress address; + + /* Dummy constructor for JAXB */ + @SuppressWarnings("unused") + private SetNextHop() { + } + + public SetNextHop(InetAddress address) { + type = ActionType.SET_NEXT_HOP; + this.address = address; + } + + public InetAddress getAddress() { + return address; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((address == null) ? 0 : address.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!super.equals(obj)) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + SetNextHop other = (SetNextHop) obj; + if (address == null) { + if (other.address != null) { + return false; + } + } else if (!address.equals(other.address)) { + return false; + } + return true; + } + + @Override + public String toString() { + return type + "[" + address.toString() + "]"; + } +} diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java index 691ddc9356..2591d931e1 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java @@ -40,10 +40,11 @@ public abstract class NetUtils { * @return the integer number */ public static int byteArray4ToInt(byte[] ba) { - if (ba == null || ba.length != 4) + if (ba == null || ba.length != 4) { return 0; - return (int) ((0xff & ba[0]) << 24 | (0xff & ba[1]) << 16 - | (0xff & ba[2]) << 8 | (0xff & ba[3])); + } + return (0xff & ba[0]) << 24 | (0xff & ba[1]) << 16 + | (0xff & ba[2]) << 8 | (0xff & ba[3]); } /** @@ -131,7 +132,7 @@ public abstract class NetUtils { int intMask = 0; int numBytes = prefixMask.length; for (int i = 0; i < numBytes; i++) { - intMask |= ((int) prefixMask[i] & 0xff) << (8 * (numBytes - 1 - i)); + intMask |= (prefixMask[i] & 0xff) << (8 * (numBytes - 1 - i)); } int bit = 1; @@ -219,13 +220,13 @@ public abstract class NetUtils { if (isAny(testAddress) || isAny(filterAddress)) { return false; } - + int testMaskLen = (testMask != null) ? NetUtils.getSubnetMaskLength(testMask.getAddress()) : 0; int filterMaskLen = (filterMask != null) ? NetUtils.getSubnetMaskLength(filterMask.getAddress()) : 0; - + int testPrefixLen = (testAddress instanceof Inet6Address) ? (128 - testMaskLen) : (32 - testMaskLen); int filterPrefixLen = (filterAddress instanceof Inet6Address) ? (128 - filterMaskLen) : (32 - filterMaskLen); - + // Mask length check. Test mask has to be more specific than filter one if (testPrefixLen < filterPrefixLen) { return true; @@ -292,8 +293,9 @@ public abstract class NetUtils { * @return */ public static boolean isIPv4AddressValid(String cidr) { - if (cidr == null) + if (cidr == null) { return false; + } String values[] = cidr.split("/"); Pattern ipv4Pattern = Pattern @@ -319,8 +321,9 @@ public abstract class NetUtils { * @return */ public static boolean isIPv6AddressValid(String cidr) { - if (cidr == null) + if (cidr == null) { return false; + } String values[] = cidr.split("/"); try { @@ -341,29 +344,42 @@ public abstract class NetUtils { } return true; } - + + /** + * Checks if the passed IP address in string form is a valid v4 or v6 + * address. The address may specify a mask at the end as "/MMM" + * + * @param cidr + * the v4 or v6 address as IP/MMM + * @return + */ + public static boolean isIPAddressValid(String cidr) { + return NetUtils.isIPv4AddressValid(cidr) + || NetUtils.isIPv6AddressValid(cidr); + } + /* - * Following utilities are useful when you need to + * Following utilities are useful when you need to * compare or bit shift java primitive type variable * which are inerently signed */ /** * Returns the unsigned value of the passed byte variable - * + * * @param b the byte value * @return the int variable containing the unsigned byte value */ public static int getUnsignedByte(byte b) { - return (b > 0)? (int)b : ((int)b & 0x7F | 0x80); + return (b > 0)? (int)b : (b & 0x7F | 0x80); } - + /** * Return the unsigned value of the passed short variable - * + * * @param s the short value * @return the int variable containing the unsigned short value */ public static int getUnsignedShort(short s) { - return (s > 0)? (int)s : ((int)s & 0x7FFF | 0x8000); + return (s > 0)? (int)s : (s & 0x7FFF | 0x8000); } } diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/action/ActionTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/action/ActionTest.java index 6858494283..4fd5979003 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/action/ActionTest.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/action/ActionTest.java @@ -156,7 +156,7 @@ public class ActionTest { try { ip = InetAddress.getByName("2001:420:281:1003:f2de:f1ff:fe71:728d"); } catch (UnknownHostException e) { - logger.error("",e); + logger.error("", e); } action = new SetNwSrc(ip); Assert.assertTrue(action.isValid()); @@ -172,7 +172,7 @@ public class ActionTest { action = new SetNwTos(0x40); Assert.assertFalse(action.isValid()); - + action = new SetNwTos(0xff1); Assert.assertFalse(action.isValid()); @@ -206,6 +206,29 @@ public class ActionTest { Assert.assertFalse(action.isValid()); } + @Test + public void testNextHopActionCreation() { + SetNextHop action = null; + + InetAddress ip = null; + try { + ip = InetAddress.getByName("171.71.9.52"); + } catch (UnknownHostException e) { + logger.error("", e); + } + + action = new SetNextHop(ip); + Assert.assertTrue(action.getAddress().equals(ip)); + + try { + ip = InetAddress.getByName("2001:420:281:1003:f2de:f1ff:fe71:728d"); + } catch (UnknownHostException e) { + logger.error("", e); + } + action = new SetNextHop(ip); + Assert.assertTrue(action.getAddress().equals(ip)); + } + @Test public void testActionList() { List actions = new ArrayList(); diff --git a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/utils/NetUtilsTest.java b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/utils/NetUtilsTest.java index 2d16afbd3d..599f97448a 100644 --- a/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/utils/NetUtilsTest.java +++ b/opendaylight/sal/api/src/test/java/org/opendaylight/controller/sal/utils/NetUtilsTest.java @@ -273,7 +273,7 @@ public class NetUtilsTest { .isIPv6AddressValid("fe80:::0:0:0:204:61ff:fe9d/-1")); //not valid both } - + @Test public void testInetAddressConflict() throws UnknownHostException { @@ -318,4 +318,17 @@ public class NetUtilsTest { InetAddress.getByName("255.255.0.0"))); } + + @Test + public void testIPAddressValidity() { + Assert.assertFalse(NetUtils.isIPAddressValid(null)); + Assert.assertFalse(NetUtils.isIPAddressValid("abc")); + Assert.assertFalse(NetUtils.isIPAddressValid("1.1.1")); + Assert.assertFalse(NetUtils.isIPAddressValid("1.1.1.1/49")); + + Assert.assertTrue(NetUtils.isIPAddressValid("1.1.1.1")); + Assert.assertTrue(NetUtils.isIPAddressValid("1.1.1.1/32")); + Assert.assertTrue(NetUtils + .isIPAddressValid("2001:420:281:1004:407a:57f4:4d15:c355")); + } } -- 2.36.6