Skip to content

Commit 401af8b

Browse files
IPNet: support address format for masks, add text constructor for IP6Net.
1 parent d0a7f52 commit 401af8b

File tree

3 files changed

+86
-44
lines changed

3 files changed

+86
-44
lines changed

code/include/swoc/IPRange.h

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -656,8 +656,8 @@ class IP4Net {
656656
*/
657657
bool load(swoc::TextView text);
658658

659-
/// @return @c true if the network is valid, @c false if not.
660-
bool is_valid() const;
659+
/// @return @c true if the network contains no addresses (is invalid).
660+
bool empty() const;
661661

662662
/// @return Network address - smallest address in the network.
663663
IP4Addr min() const;
@@ -711,15 +711,24 @@ class IP6Net {
711711
*/
712712
IP6Net(IP6Addr addr, IPMask mask);
713713

714+
/** Construct from text.
715+
*
716+
* @param text Network description.
717+
*
718+
* The format must be "addr/mask" where "addr" is a valid address and mask is either a single
719+
* number for the mask width (CIDR) or a mask in address notation.
720+
*/
721+
IP6Net(TextView text) { this->load(text); }
722+
714723
/** Parse network as @a text.
715724
*
716725
* @param text String describing the network in CIDR format.
717726
* @return @c true if a valid string, @c false if not.
718727
*/
719728
bool load(swoc::TextView text);
720729

721-
/// @return @c true if the network is valid, @c false if not.
722-
bool is_valid() const;
730+
/// @return @c true if the network contains no addresses (is invalid).
731+
bool empty() const;
723732

724733
/// @return Network address - smallest address in the network.
725734
IP6Addr min() const;
@@ -779,7 +788,7 @@ class IPNet {
779788
*/
780789
IPNet(IPAddr const &addr, IPMask const &mask);
781790

782-
/** Construct from string.
791+
/** Construct from text.
783792
*
784793
* @param text Network description.
785794
*
@@ -795,8 +804,8 @@ class IPNet {
795804
*/
796805
bool load(swoc::TextView text);
797806

798-
/// @return @c true if the network is valid, @c false if not.
799-
bool is_valid() const;
807+
/// @return @c true if the network contains no addresses (is invalid).
808+
bool empty() const;
800809

801810
/// @return Network address - smallest address in the network.
802811
IPAddr min() const;
@@ -1887,8 +1896,8 @@ IP4Net::mask() const {
18871896
}
18881897

18891898
inline bool
1890-
IP4Net::is_valid() const {
1891-
return _mask.is_valid();
1899+
IP4Net::empty() const {
1900+
return ! _mask.is_valid();
18921901
}
18931902

18941903
inline IP4Addr
@@ -1931,8 +1940,8 @@ IP6Net::mask() const {
19311940
}
19321941

19331942
inline bool
1934-
IP6Net::is_valid() const {
1935-
return _mask.is_valid();
1943+
IP6Net::empty() const {
1944+
return ! _mask.is_valid();
19361945
}
19371946

19381947
inline IP6Addr
@@ -1974,8 +1983,8 @@ inline IPNet::IPNet(TextView text) {
19741983
}
19751984

19761985
inline bool
1977-
IPNet::is_valid() const {
1978-
return _mask.is_valid();
1986+
IPNet::empty() const {
1987+
return ! _mask.is_valid();
19791988
}
19801989

19811990
inline IPAddr

code/src/swoc_ip.cc

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -842,18 +842,20 @@ auto IPSrv::assign(const sockaddr *sa) -> self_type & {
842842

843843
bool
844844
IP4Net::load(TextView text) {
845-
auto idx = text.find('/');
846-
if (idx != text.npos) {
847-
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
848-
IP4Addr addr;
849-
if (addr.load(text.substr(0, idx))) { // load the address
850-
IPMask mask;
851-
text.remove_prefix(idx + 1); // drop address and separator.
852-
if (mask.load(text)) {
853-
this->assign(addr, mask);
854-
return true;
845+
if (auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) {
846+
IPMask mask;
847+
bool mask_p = mask.load(mask_text);
848+
if (IP4Addr addr; addr.load(text)) {
849+
if (!mask_p) {
850+
if (IP4Addr m ; m.load(mask_text)) {
851+
mask = IPMask::mask_for(m);
852+
mask_p = (m == mask.as_ip4()); // must be an actual mask like address.
855853
}
856854
}
855+
if (mask_p) {
856+
this->assign(addr, mask);
857+
return true;
858+
}
857859
}
858860
}
859861

@@ -863,18 +865,20 @@ IP4Net::load(TextView text) {
863865

864866
bool
865867
IP6Net::load(TextView text) {
866-
auto idx = text.find('/');
867-
if (idx != text.npos) {
868-
if (idx + 1 < text.size()) { // must have something past the separator or it's bogus.
869-
IP6Addr addr;
870-
if (addr.load(text.substr(0, idx))) { // load the address
871-
IPMask mask;
872-
text.remove_prefix(idx + 1); // drop address and separator.
873-
if (mask.load(text)) {
874-
this->assign(addr, mask);
875-
return true;
868+
if (auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) {
869+
IPMask mask;
870+
bool mask_p = mask.load(mask_text);
871+
if (IP6Addr addr; addr.load(text)) {
872+
if (!mask_p) {
873+
if (IP6Addr m ; m.load(mask_text)) {
874+
mask = IPMask::mask_for(m);
875+
mask_p = (m == mask.as_ip6()); // must be an actual mask like address.
876876
}
877877
}
878+
if (mask_p) {
879+
this->assign(addr, mask);
880+
return true;
881+
}
878882
}
879883
}
880884

@@ -886,11 +890,27 @@ bool
886890
IPNet::load(TextView text) {
887891
if ( auto mask_text = text.split_suffix_at('/') ; !mask_text.empty() ) {
888892
IPMask mask;
889-
if (mask.load(mask_text)) {
890-
if (IP6Addr a6; a6.load(text)) { // load the address
893+
bool mask_p = mask.load(mask_text);
894+
895+
if (IP6Addr a6 ; a6.load(text)) { // load the address
896+
if (!mask_p) {
897+
if (IP6Addr m ; m.load(mask_text)) {
898+
mask = IPMask::mask_for(m);
899+
mask_p = (m == mask.as_ip6()); // must be an actual mask like address.
900+
}
901+
}
902+
if (mask_p) {
891903
this->assign(a6, mask);
892904
return true;
893-
} else if (IP4Addr a4; a4.load(text)) {
905+
}
906+
} else if (IP4Addr a4 ; a4.load(text)) {
907+
if (!mask_p) {
908+
if (IP4Addr m ; m.load(mask_text)) {
909+
mask = IPMask::mask_for(m);
910+
mask_p = (m == mask.as_ip4()); // must be an actual mask like address.
911+
}
912+
}
913+
if (mask_p) {
894914
this->assign(a4, mask);
895915
return true;
896916
}

unit_tests/test_ip.cc

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -353,19 +353,32 @@ TEST_CASE("IP Net and Mask", "[libswoc][ip][ipnet]") {
353353
REQUIRE(IP4Addr::MIN == IPMask(0).as_ip4());
354354
REQUIRE(IPMask(24).as_ip4() == a24);
355355

356+
SECTION("addr as mask") {
357+
swoc::IP4Net n1{"10.0.0.0/255.255.0.0"};
358+
CHECK_FALSE(n1.empty());
359+
REQUIRE(n1.mask().width() == 16);
360+
361+
swoc::IP6Net n2{"BEEF:1337:dead::/FFFF:FFFF:FFFF:C000::"};
362+
CHECK_FALSE(n2.empty());
363+
REQUIRE(n2.mask().width() == 50);
364+
365+
swoc::IPNet n3{"10.0.0.0/255.255.0.0"};
366+
CHECK_FALSE(n3.empty());
367+
REQUIRE(n3.mask().width() == 16);
368+
369+
swoc::IPNet n4{"BEEF:1337:dead::/FFFF:FFFF:FFFF:C000::"};
370+
CHECK_FALSE(n4.empty());
371+
REQUIRE(n4.mask().width() == 50);
372+
373+
swoc::IPNet n5{"BEEF:1337:dead::/FFFF:FFFF:FFFF:000C::"};
374+
REQUIRE(n5.empty()); // mask address isn't a valid mask.
375+
}
376+
356377
swoc::IP4Net n1{"0/1"};
357378
auto nr1 = n1.as_range();
358379
REQUIRE(nr1.min() == IP4Addr::MIN);
359380
REQUIRE(nr1.max() == IP4Addr("127.255.255.255"));
360381

361-
#if 0
362-
swoc::IP4Net n2{"10.0.0.0/255.255.0.0"};
363-
REQUIRE(n2.mask().width() == 16);
364-
365-
swoc::IPNet n3{"10.0.0.0/255.255.0.0"};
366-
REQUIRE(n3.mask().width() == 16);
367-
#endif
368-
369382
IP4Addr a{"8.8.8.8"};
370383
swoc::IP4Net n4{a, IPMask{32}};
371384
auto nr4 = n4.as_range();

0 commit comments

Comments
 (0)