Advertisement
Guest User

allowedips.cpp

a guest
Feb 7th, 2021
249
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 9.72 KB | None | 0 0
  1. // C++11 application to calculate wireguard AllowedIPs by PG.
  2. // License: Public Domain
  3. // To compile: g++ -std=c++11 ./allowedips.cpp -o allowedips
  4. //
  5. // Usage: allowedips [+-]spec [+-]spec [+-]spec ...
  6. // where spec can be
  7. //     1.2.3.4         // ipv4 address\n");
  8. //     1.2.3.4/32      // ipv4 address and mask\n");
  9. //     1.2.3.4-2.3.4.5 // ipv4 address range\n");
  10. //     2345:0425:2CA1:0000:0000:0567:5673:23b5 // ipv6 address\n");
  11. //     ::/0            // ipv6 address and mask\n");
  12. //     2345:0425:2CA1:0000:0000:0567:5673:23b5-2345:0425:2CA1:0000:0000:0567:5673:23b6 // ipv6 address range\n");
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <stdint.h>
  17. #include <string.h>
  18. #include <netinet/in.h>
  19. #include <arpa/inet.h>
  20.  
  21. #include <string>
  22. #include <map>
  23. #include <vector>
  24.  
  25. #define IS_LITTLE_ENDIAN (htonl(47)!=47)
  26.  
  27. struct ip {
  28.     sockaddr_storage sa={};
  29.  
  30.     int family() const { return sa.ss_family; }
  31.  
  32.     const sockaddr_in &ip4() const { return *(sockaddr_in*)&sa; }
  33.     const sockaddr_in6 &ip6() const { return *(sockaddr_in6*)&sa; }
  34.  
  35.     uint8_t *s_addr() const { return is_ip4() ? (uint8_t*)&ip4().sin_addr.s_addr : (uint8_t*)&ip6().sin6_addr.s6_addr[0]; }
  36.     uint8_t *s_addr_byte_ptr(const int idx) const { return s_addr() + (IS_LITTLE_ENDIAN ? s_addr_len()-1-idx : idx); }
  37.     uint8_t s_addr_byte(const int idx) const { return *s_addr_byte_ptr(idx); }
  38.     uint8_t s_addr_bit(const int idx) const { return (1<<(idx&7))&*s_addr_byte_ptr(idx>>3); }
  39.     size_t s_addr_len() const { return is_ip4() ? sizeof(ip4().sin_addr.s_addr) : sizeof(ip6().sin6_addr.s6_addr); }
  40.     int s_addr_bits() const { return 8*s_addr_len(); }
  41.     int s_addr_cmp(const ip &o) const {
  42.         for (int i=s_addr_len()-1;i>=0;i--) {
  43.             if (s_addr_byte(i)<o.s_addr_byte(i)) { return -1; }
  44.             if (s_addr_byte(i)>o.s_addr_byte(i)) { return 1; }
  45.         }
  46.         return 0;
  47.     }
  48.     bool is_ip4() const { return family()==AF_INET; }
  49.     bool operator==(const ip &o) const { return s_addr_cmp(o)==0; }
  50.     bool operator!=(const ip &o) const { return s_addr_cmp(o)!=0; }
  51.     bool operator<(const ip &o) const  { return s_addr_cmp(o)<0; }
  52.     bool operator>(const ip &o) const  { return s_addr_cmp(o)>0; }
  53.     bool operator<=(const ip &o) const { return s_addr_cmp(o)<=0; }
  54.     bool operator>=(const ip &o) const { return s_addr_cmp(o)>=0; }
  55.  
  56.     bool set(const std::string &s) {
  57.         sa.ss_family=AF_INET6;
  58.         if (inet_pton(sa.ss_family,s.c_str(),s_addr())==1)
  59.             return true;
  60.         sa.ss_family=AF_INET;
  61.         return inet_pton(sa.ss_family,s.c_str(),s_addr())==1;
  62.     }
  63.  
  64.     bool and_mask(const int mask_width) { // 192.168.1.x/24 => 192.168.1.0
  65.         if (mask_width<0 || mask_width>s_addr_bits())
  66.             return false;
  67.         for (int i=0;i<s_addr_bits()-mask_width;i++)
  68.             *s_addr_byte_ptr(i>>3)&=~(1<<(i&7));
  69.         return true;
  70.     }
  71.     bool or_mask(const int mask_width) { // 192.168.1.x/24 => 192.168.1.255
  72.         if (mask_width<0 || mask_width>s_addr_bits())
  73.             return false;
  74.         for (int i=0;i<s_addr_bits()-mask_width;i++)
  75.             *s_addr_byte_ptr(i>>3)|=1<<(i&7);
  76.         return true;
  77.     }
  78.  
  79.     ip plus1() const { // return IP address + 1
  80.         ip next(*this);
  81.         for (int i=0;i<s_addr_len();i++) {
  82.             if (++*next.s_addr_byte_ptr(i)) break;
  83.         }
  84.         return next;
  85.     }
  86.     ip minus1() const { // return IP address - 1
  87.         ip prev(*this);
  88.         for (int i=0;i<s_addr_len();i++) {
  89.             if (--*prev.s_addr_byte_ptr(i)!=0xff) break;
  90.         }
  91.         return prev;
  92.     }
  93.  
  94.     std::string to_str() const {
  95.         char str[INET6_ADDRSTRLEN];
  96.         return inet_ntop(family(),s_addr(),str,INET6_ADDRSTRLEN) ? str : "";
  97.     }
  98. };
  99.  
  100. struct range {
  101.     ip start,end;
  102.  
  103.     bool set(const std::string &spec) {
  104.         size_t msep=spec.find('/'); // 1.2.3.4/32
  105.         if (msep!=std::string::npos) {
  106.             int mask_width=atoi(spec.substr(msep+1).c_str());
  107.             if (!start.set(spec.substr(0,msep)))
  108.                 return false;
  109.             if (!start.and_mask(mask_width))
  110.                 return false;
  111.             end=start;
  112.             if (!end.or_mask(mask_width))
  113.                 return false;
  114.             return true;
  115.         }
  116.         size_t rsep=spec.find('-'); // 1.2.3.4-2.3.4.5
  117.         if (rsep!=std::string::npos) {
  118.             if (!start.set(spec.substr(0,rsep)))
  119.                 return false;
  120.             if (!end.set(spec.substr(rsep+1)))
  121.                 return false;
  122.             if (end<start || start.family()!=end.family())
  123.                 return false;
  124.             return true;
  125.         }
  126.  
  127.         if (!start.set(spec)) // 1.2.3.4
  128.             return false;
  129.         end=start;
  130.         return true;
  131.     }
  132.  
  133.     bool is_ip4() const { return start.is_ip4(); }
  134.     int family() const { return start.family(); }
  135.  
  136.     std::string to_str() const { return start.to_str()+'-'+end.to_str(); }
  137. };
  138.  
  139. struct RangeSet {
  140.     int family;
  141.     std::map<ip,range> ranges;
  142.  
  143.     RangeSet(const int family=AF_INET) : family(family) { }
  144.  
  145.     bool remove_range(const range &b) {
  146.         for (auto i=ranges.begin();i!=ranges.end();) {
  147.             range &a=i->second;
  148.  
  149.             if (b.start>a.end) { i++; continue; }
  150.             if (b.end<a.start) break;
  151.  
  152.             if (b.start<=a.start && b.end>=a.end) { // remove a completely
  153.                 auto x=i++;
  154.                 ranges.erase(x);
  155.             } else if (b.start>a.start && b.end<a.end) { // remove range in middle of a
  156.                 range n;
  157.                 n.start=b.end.plus1();
  158.                 n.end=a.end;
  159.                 ranges[n.start]=n;
  160.                 a.end=b.start.minus1();
  161.                 break;
  162.             } else if (b.start<=a.start) { // trim left
  163.                 a.start=b.end.plus1();
  164.                 break;
  165.             } else { // trim right
  166.                 a.end=b.start.minus1();
  167.                 i++;
  168.             }
  169.         }
  170.         return true;
  171.     }
  172.     bool remove_range(const std::string &spec) {
  173.         range b;
  174.         return b.set(spec) && b.family()==family ? remove_range(b) : false;
  175.     }
  176.  
  177.     bool add_range(const std::string &spec) {
  178.         range b;
  179.         if (!b.set(spec) || b.family()!=family)
  180.             return false;
  181.  
  182.         // remove any overlaps with existing ranges
  183.         if (!remove_range(b))
  184.             return false;
  185.  
  186.         // add the new range
  187.         ranges[b.start]=b;
  188.  
  189.         // merge any adjacent ranges
  190.         if (ranges.size()>1) {
  191.             for (auto i=ranges.begin();;) {
  192.                 auto next=i;
  193.                 if (++next==ranges.end())
  194.                     break;
  195.                 range &a=i->second,&b=next->second;
  196.                 if (a.end.plus1()==b.start) {
  197.                     range n=b;
  198.                     n.start=a.start;
  199.                     ranges.erase(i);
  200.                     ranges.erase(next);
  201.                     next=ranges.insert({n.start,n}).first;
  202.                 }
  203.                 i=next;
  204.             }
  205.         }
  206.         return true;
  207.     }
  208.  
  209.     std::multimap<int,ip> get_cidrs() {
  210.         std::multimap<int,ip> cidrs; // width,ip
  211.         const auto ranges_copy=ranges;
  212.  
  213.         // Find CIDR ranges by iteratively finding and removing
  214.         // the shortest CIDR mask possible from each range
  215.         while (!ranges.empty()) {
  216.             range &r=ranges.begin()->second;
  217.  
  218.             // find shortest CIDR mask (largest IP range) that fits within range r
  219.             range x;
  220.             int mask_width=0;
  221.             for (;;) {
  222.                 x.start=x.end=r.start;
  223.                 x.start.and_mask(mask_width);
  224.                 x.end.or_mask(mask_width);
  225.                 if (x.start>=r.start && x.end<=r.end)
  226.                     break;
  227.                 mask_width++;
  228.             }
  229.             cidrs.insert({mask_width,x.start});
  230.  
  231.             remove_range(x);
  232.         }
  233.  
  234.         ranges=ranges_copy; // restore range data
  235.         return cidrs;
  236.     }
  237. };
  238.  
  239. static RangeSet ip4ranges(AF_INET),ip6ranges(AF_INET6);
  240.  
  241. static void output_cidrs() {
  242.  
  243.     printf("AllowedIPs =");
  244.  
  245.     auto ip4cidrs=ip4ranges.get_cidrs();
  246.     int count=0;
  247.     for (auto &p : ip4cidrs)
  248.         printf("%s%s/%d",count++?", ":" ",p.second.to_str().c_str(),p.first);
  249.  
  250.     auto ip6cidrs=ip6ranges.get_cidrs();
  251.     for (auto &p : ip6cidrs)
  252.         printf("%s%s/%d",count++?", ":" ",p.second.to_str().c_str(),p.first);
  253.  
  254.     printf("\n");
  255. }
  256.  
  257. int main(int argc,char **argv) {
  258.     bool err=argc<2;
  259.  
  260.     for (int i=1;!err && i<argc;i++) {
  261.         bool ok=false;
  262.         if (argv[i][0]=='+') {
  263.             ok=ip6ranges.add_range(argv[i]+1);
  264.             if (!ok)
  265.                 ok=ip4ranges.add_range(argv[i]+1);
  266.         } else if (argv[i][0]=='-') {
  267.             ok=ip6ranges.remove_range(argv[i]+1);
  268.             if (!ok)
  269.                 ok=ip4ranges.remove_range(argv[i]+1);
  270.         }
  271.         if (!ok) {
  272.             fprintf(stderr,"error - bad arg: %s\n",argv[i]);
  273.             err=true;
  274.         }
  275.     }
  276.    
  277.     if (err) {
  278.         fprintf(stderr,"Usage: %s [+-]spec [+-]spec [+-]spec ...\n",argv[0]);
  279.         fprintf(stderr,"    where spec can be\n");
  280.         fprintf(stderr,"    1.2.3.4         // ipv4 address\n");
  281.         fprintf(stderr,"    1.2.3.4/32      // ipv4 address and mask\n");
  282.         fprintf(stderr,"    1.2.3.4-2.3.4.5 // ipv4 address range\n");
  283.         fprintf(stderr,"    2345:0425:2CA1:0000:0000:0567:5673:23b5 // ipv6 address\n");
  284.         fprintf(stderr,"    ::/0            // ipv6 address and mask\n");
  285.         fprintf(stderr,"    2345:0425:2CA1:0000:0000:0567:5673:23b5-2345:0425:2CA1:0000:0000:0567:5673:23b6 // ipv6 address range\n");
  286.         exit(EXIT_FAILURE);
  287.     }
  288.    
  289.     output_cidrs();
  290.  
  291.     return 0;
  292. }
  293.  
  294.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement