]> git.ipfire.org Git - people/ms/network.git/blobdiff - src/inetcalc.c
man: network-route-static: Fix name
[people/ms/network.git] / src / inetcalc.c
index ba692542aba77300a5088edc406fc08e44dabc5a..1841c84065975b7e9beff194f8a67bc81118d12a 100644 (file)
@@ -28,6 +28,8 @@
 #include <string.h>
 #include <sys/socket.h>
 
+#include <network/libnetwork.h>
+
 typedef struct ip_address {
        int family;
        struct in6_addr addr;
@@ -116,6 +118,19 @@ static int ip_address_parse_prefix(ip_address_t* ip, const int family, const cha
        return r;
 }
 
+static int default_prefix(const int family) {
+       switch (family) {
+               case AF_INET6:
+                       return 128;
+
+               case AF_INET:
+                       return 32;
+
+               default:
+                       return -1;
+       }
+}
+
 static int ip_address_parse_simple(ip_address_t* ip, const int family, const char* address) {
        assert(family == AF_INET || family == AF_INET6);
 
@@ -152,7 +167,7 @@ static int ip_address_parse_simple(ip_address_t* ip, const int family, const cha
        if (prefix)
                r = ip_address_parse_prefix(ip, family, prefix);
        else
-               ip->prefix = -1;
+               ip->prefix = default_prefix(family);
 
        return r;
 }
@@ -180,7 +195,7 @@ static int ip_address_eq(const ip_address_t* a1, const ip_address_t* a2) {
        if (a1->family != a2->family)
                return 1;
 
-       if (a1->addr.s6_addr != a2->addr.s6_addr)
+       if (!IN6_ARE_ADDR_EQUAL(&a1->addr, &a2->addr))
                return 1;
 
        if (a1->prefix != a2->prefix)
@@ -193,12 +208,28 @@ static int ip_address_gt(const ip_address_t* a1, const ip_address_t* a2) {
        if (a1->family != a2->family || a1->prefix != a2->prefix)
                return -1;
 
-       if (a1->addr.s6_addr > a2->addr.s6_addr)
+       if (memcmp(&a1->addr.s6_addr, &a2->addr.s6_addr, sizeof(a1->addr.s6_addr)) > 0)
                return 0;
 
        return 1;
 }
 
+static int ip_address_ge(const ip_address_t* a1, const ip_address_t* a2) {
+       int r = ip_address_eq(a1, a2);
+       if (r <= 0)
+               return r;
+
+       return ip_address_gt(a1, a2);
+}
+
+static int ip_address_le(const ip_address_t* a1, const ip_address_t* a2) {
+       int r = ip_address_eq(a1, a2);
+       if (r <= 0)
+               return r;
+
+       return !ip_address_gt(a1, a2);
+}
+
 static int ip_address_format_string(char* buffer, size_t size, const ip_address_t* ip) {
        assert(ip->family == AF_INET || ip->family == AF_INET6);
 
@@ -216,7 +247,10 @@ static void ip_address_print(const ip_address_t* ip) {
        if (r)
                return;
 
-       if (ip->prefix >= 0) {
+       int address_prefix = default_prefix(ip->family);
+
+       // Only print prefix when it is not the default one
+       if (ip->prefix != address_prefix) {
                size_t len = strlen(buffer);
                snprintf(buffer + len, sizeof(buffer) - len, "/%d", ip->prefix);
        }
@@ -224,28 +258,69 @@ static void ip_address_print(const ip_address_t* ip) {
        printf("%s\n", buffer);
 }
 
-static void ip_address_make_network(ip_address_t* net, const ip_address_t* ip) {
-       assert(ip->prefix >= 0);
+static void ip_address_get_first_address(ip_address_t* first, const ip_address_t* network) {
+       assert(network->prefix >= 0);
 
-       struct in6_addr mask = prefix_to_bitmask(ip->prefix);
+       struct in6_addr mask = prefix_to_bitmask(network->prefix);
 
-       net->family = ip->family;
-       net->prefix = ip->prefix;
+       first->family = network->family;
+       first->prefix = default_prefix(network->family);
 
        for (int i = 0; i < 16; i++)
-               net->addr.s6_addr[i] = ip->addr.s6_addr[i] & mask.s6_addr[i];
+               first->addr.s6_addr[i] = network->addr.s6_addr[i] & mask.s6_addr[i];
 }
 
-static void ip_address_make_broadcast(ip_address_t* broadcast, const ip_address_t* ip) {
-       assert(ip->family == AF_INET && ip->prefix >= 0);
+static void ip_address_get_last_address(ip_address_t* last, const ip_address_t* network) {
+       assert(network->prefix >= 0);
 
-       struct in6_addr mask = prefix_to_bitmask(ip->prefix);
+       struct in6_addr mask = prefix_to_bitmask(network->prefix);
 
-       broadcast->family = ip->family;
-       broadcast->prefix = ip->prefix;
+       last->family = network->family;
+       last->prefix = default_prefix(network->family);
 
        for (int i = 0; i < 16; i++)
-               broadcast->addr.s6_addr[i] = ip->addr.s6_addr[i] | ~mask.s6_addr[i];
+               last->addr.s6_addr[i] = network->addr.s6_addr[i] | ~mask.s6_addr[i];
+}
+
+static void ip_address_make_network(ip_address_t* net, const ip_address_t* network) {
+       ip_address_get_first_address(net, network);
+
+       // Copy the prefix
+       net->prefix = network->prefix;
+}
+
+static void ip_address_make_broadcast(ip_address_t* broadcast, const ip_address_t* network) {
+       assert(network->family == AF_INET);
+
+       ip_address_get_last_address(broadcast, network);
+
+       // Copy the prefix
+       broadcast->prefix = network->prefix;
+}
+
+static int ip_address_is_subset(const ip_address_t* network1, const ip_address_t* network2) {
+       ip_address_t first1;
+       ip_address_t first2;
+       ip_address_t last1;
+       ip_address_t last2;
+
+       // Get the first address of the networks
+       ip_address_get_first_address(&first1, network1);
+       ip_address_get_first_address(&first2, network2);
+
+       // Get the highest address in both networks
+       ip_address_get_last_address(&last1, network1);
+       ip_address_get_last_address(&last2, network2);
+
+       // The start address must be in the network
+       if (ip_address_ge(&first1, &first2) == 0 && ip_address_le(&first1, &last2) == 0) {
+               // The end address must be in the network, too
+               if (ip_address_ge(&last1, &first2) == 0 && ip_address_le(&last1, &last2) == 0) {
+                       return 0;
+               }
+       }
+
+       return 1;
 }
 
 static int action_check(const int family, const char* address) {
@@ -255,8 +330,11 @@ static int action_check(const int family, const char* address) {
        if (r)
                return r;
 
-       // No prefix allowed
-       return (ip.prefix >= 0);
+       // If the prefix is the host prefix this is a host address
+       if (ip.prefix == default_prefix(family))
+               return 0;
+
+       return 1;
 }
 
 static int action_equal(const int family, const char* addr1, const char* addr2) {
@@ -370,6 +448,28 @@ static int action_prefix(const int family, const char* addr1, const char* addr2)
        return 0;
 }
 
+static int action_subset(const int family, const char* address1, const char* address2) {
+       int r;
+       ip_address_t network1;
+       ip_address_t network2;
+
+       // Parse both networks and/or IP addresses
+       r = ip_address_parse(&network1, family, address1);
+       if (r)
+               return r;
+
+       r = ip_address_parse(&network2, family, address2);
+       if (r)
+               return r;
+
+       if (network1.family != network2.family) {
+               fprintf(stderr, "Address family of both arguments must match\n");
+               return -1;
+       }
+
+       return ip_address_is_subset(&network1, &network2);
+}
+
 enum actions {
        AC_UNSPEC = 0,
        AC_BROADCAST,
@@ -378,6 +478,7 @@ enum actions {
        AC_FORMAT,
        AC_GREATER,
        AC_NETWORK,
+       AC_SUBSET,
        AC_PREFIX,
 };
 
@@ -400,6 +501,7 @@ static struct option long_options[] = {
        {"ipv6-only",         no_argument,       0, '6'},
        {"network",           no_argument,       0, 'n'},
        {"prefix",            no_argument,       0, 'p'},
+       {"subset",            no_argument,       0, 's'},
        {"verbose",           no_argument,       0, 'v'},
        {0, 0, 0, 0}
 };
@@ -413,7 +515,7 @@ int main(int argc, char** argv) {
        int family = AF_UNSPEC;
 
        while (1) {
-               int c = getopt_long(argc, argv, "46bcefgnpv", long_options, &option_index);
+               int c = getopt_long(argc, argv, "46bcefgnpsviV", long_options, &option_index);
                if (c == -1)
                        break;
 
@@ -471,10 +573,20 @@ int main(int argc, char** argv) {
                                required_arguments = 2;
                                break;
 
+                       case 's':
+                               set_action(&action, AC_SUBSET);
+                               required_arguments = 2;
+                               break;
+
                        case 'v':
                                verbose = 1;
                                break;
 
+                       case 'V':
+                               printf("%s\n", network_version());
+                               exit(0);
+                               break;
+
                        case '?':
                                break;
 
@@ -558,6 +670,18 @@ int main(int argc, char** argv) {
                        r = action_network(family, argv[0]);
                        break;
 
+               case AC_SUBSET:
+                       r = action_subset(family, argv[0], argv[1]);
+
+                       if (verbose) {
+                               if (r == 0)
+                                       printf("%s is a subset of %s\n", argv[0], argv[1]);
+                               else if (r > 0)
+                                       printf("%s is not a subset of %s\n", argv[0], argv[1]);
+                       }
+
+                       break;
+
                case AC_PREFIX:
                        r = action_prefix(family, argv[0], argv[1]);
                        break;