Переглянути джерело

modules/ipops: Added new exported function is_in_subnet()

- Feature added by Hugh Waite @ Crocodile RCS Ltd
Peter Dunkley 13 роки тому
батько
коміт
76d2424a79

+ 24 - 0
modules/ipops/README

@@ -35,6 +35,7 @@ Iñaki Baz Castillo
               4.7. compare_ips (ip1, ip2)
               4.8. compare_pure_ips (ip1, ip2)
               4.9. is_ip_rfc1918 (ip)
+              4.10. is_in_subnet (ip, subnet)
 
    List of Examples
 
@@ -47,6 +48,7 @@ Iñaki Baz Castillo
    1.7. compare_ips usage
    1.8. compare_pure_ips usage
    1.9. is_ip_rfc1918 usage
+   1.10. is_in_subnet usage
 
 Chapter 1. Admin Guide
 
@@ -70,6 +72,7 @@ Chapter 1. Admin Guide
         4.7. compare_ips (ip1, ip2)
         4.8. compare_pure_ips (ip1, ip2)
         4.9. is_ip_rfc1918 (ip)
+        4.10. is_in_subnet (ip, subnet)
 
 1. Overview
 
@@ -117,6 +120,7 @@ Chapter 1. Admin Guide
    4.7. compare_ips (ip1, ip2)
    4.8. compare_pure_ips (ip1, ip2)
    4.9. is_ip_rfc1918 (ip)
+   4.10. is_in_subnet (ip, subnet)
 
 4.1.  is_ip (ip)
 
@@ -299,3 +303,23 @@ if (is_ip_rfc1918("10.0.123.123")) {
   xlog("L_INFO", "it's a private IPv4\n");
 }
 ...
+
+4.10.  is_in_subnet (ip, subnet)
+
+   Returns TRUE if the first argument is an IP address within the (CIDR
+   notation) subnet in the second argument. FALSE otherwise.
+
+   Parameters:
+     * ip - string or pseudo-variable containing the ip to evaluate.
+     * subnet - string or pseudo-variable containing the (CIDR notation)
+       subnet to evaluate.
+
+   This function can be used from REQUEST_ROUTE, FAILURE_ROUTE,
+   ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE.
+
+   Example 1.10.  is_in_subnet usage
+...
+if (is_in_subnet("10.0.123.123", "10.0.123.1/24")) {
+  xlog("L_INFO", "it's in the subnet\n");
+}
+...

+ 133 - 0
modules/ipops/api.c

@@ -0,0 +1,133 @@
+/*
+ * $Id$
+ *
+ * Functions that process IPOPS message 
+ *
+ * Copyright (C) 2012 Hugh Waite (crocodile-rcs.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdio.h>
+
+#include "api.h"
+#include "ip_parser.h"
+
+extern int _compare_ips(char*, size_t, enum enum_ip_type, char*, size_t, enum enum_ip_type);
+extern int _ip_is_in_subnet(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type, int netmask);
+/**
+ *
+ */
+int ipopsapi_compare_ips(const str *const ip1, const str *const ip2)
+{
+  str string1 = *ip1;
+  str string2 = *ip2;
+  enum enum_ip_type ip1_type, ip2_type;
+  
+  switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      string1.s += 1;
+      string1.len -= 2;
+      ip1_type = ip_type_ipv6;
+      break;
+    default:
+      break;
+  }
+  switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      string2.s += 1;
+      string2.len -= 2;
+      ip2_type = ip_type_ipv6;
+      break;
+    default:
+      break;
+  }
+
+  if (_compare_ips(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type))
+    return 1;
+  else
+    return -1;
+}
+
+/**
+ *
+ */
+int ipopsapi_ip_is_in_subnet(const str *const ip1, const str *const ip2)
+{
+  str string1 = *ip1;
+  str string2 = *ip2;
+  enum enum_ip_type ip1_type, ip2_type;
+  char *cidr_pos = NULL;
+  int netmask = 0;
+  
+  switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      return -1;
+      break;
+    default:
+      break;
+  }
+  cidr_pos = string2.s + string2.len - 1;
+  while (cidr_pos > string2.s)
+  {
+    if (*cidr_pos == '/') break;
+    cidr_pos--;
+  }
+  if (cidr_pos == string2.s) return -1;
+  string2.len = (cidr_pos - string2.s);
+  netmask = atoi(cidr_pos+1);
+  switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      return -1;
+      break;
+    default:
+      break;
+  }
+  
+  if (_ip_is_in_subnet(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type, netmask))
+    return 1;
+  else
+    return -1;
+}
+
+/**
+ *
+ */
+int bind_ipops(ipops_api_t* api)
+{
+	if (!api) {
+		ERR("Invalid parameter value\n");
+		return -1;
+	}
+	api->compare_ips     = ipopsapi_compare_ips;
+	api->ip_is_in_subnet = ipopsapi_ip_is_in_subnet;
+
+	return 0;
+}

+ 71 - 0
modules/ipops/api.h

@@ -0,0 +1,71 @@
+/*
+ * $Id$
+ *
+ * Functions that operate on IP addresses
+ *
+ * Copyright (C) 2012 Hugh Waite (crocodile-rcs.com)
+ *
+ * This file is part of Kamailio, a free SIP server.
+ *
+ * Kamailio is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version
+ *
+ * Kamailio is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License 
+ * along with this program; if not, write to the Free Software 
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+
+#ifndef _IPOPS_API_H_
+#define _IPOPS_API_H_
+
+#include "../../sr_module.h"
+#include "../../parser/msg_parser.h"
+
+typedef int (*compare_ips_f)(const str *const, const str *const);
+int ipopsapi_compare_ips(const str *const ip1, const str *const ip2);
+
+typedef int (*ip_is_in_subnet_f)(const str *const, const str *const);
+int ipopsapi_ip_is_in_subnet(const str *const ip1, const str *const ip2);
+
+/**
+ * @brief IPOPS API structure
+ */
+typedef struct ipops_api {
+	compare_ips_f       compare_ips;
+	ip_is_in_subnet_f   ip_is_in_subnet;
+} ipops_api_t;
+
+typedef int (*bind_ipops_f)(ipops_api_t* api);
+int bind_ipops(ipops_api_t* api);
+
+/**
+ * @brief Load the IPOPS API
+ */
+static inline int ipops_load_api(ipops_api_t *api)
+{
+	bind_ipops_f bindipops;
+
+	bindipops = (bind_ipops_f)find_export("bind_ipops", 0, 0);
+	if(bindipops == 0) {
+		LM_ERR("cannot find bind_ipops\n");
+		return -1;
+	}
+	if (bindipops(api) < 0)
+	{
+		LM_ERR("cannot bind ipops api\n");
+		return -1;
+	}
+	return 0;
+}
+
+
+#endif

+ 43 - 0
modules/ipops/doc/ipops_admin.xml

@@ -478,6 +478,49 @@ if (is_ip_rfc1918("10.0.123.123")) {
 
     </section>
  
+    <section>
+      <title>
+        <function moreinfo="none">is_in_subnet (ip, subnet)</function>
+      </title>
+
+      <para>
+        Returns TRUE if the first argument is an IP address within the (CIDR notation)
+        subnet in the second argument. FALSE otherwise.
+      </para>
+
+      <para>Parameters:</para>
+
+      <itemizedlist>
+        <listitem>
+          <para>
+            <emphasis>ip</emphasis> - string or pseudo-variable containing the ip to evaluate.
+          </para>
+        </listitem>
+        <listitem>
+          <para>
+            <emphasis>subnet</emphasis> - string or pseudo-variable containing the (CIDR notation) subnet to evaluate.
+          </para>
+        </listitem>
+      </itemizedlist>
+
+      <para>
+        This function can be used from REQUEST_ROUTE, FAILURE_ROUTE, ONREPLY_ROUTE, BRANCH_ROUTE and LOCAL_ROUTE.
+      </para>
+
+      <example>
+        <title>
+          <function>is_in_subnet</function> usage
+        </title>
+        <programlisting format="linespecific">
+...
+if (is_in_subnet("10.0.123.123", "10.0.123.1/24")) {
+  xlog("L_INFO", "it's in the subnet\n");
+}
+...
+        </programlisting>
+      </example>
+
+    </section>
 
   </section>
  

+ 137 - 2
modules/ipops/ipops_mod.c

@@ -46,6 +46,7 @@
 #include "../../str.h"
 #include "../../mod_fix.h"
 #include "../../pvar.h"
+#include "api.h"
 #include "ip_parser.h"
 #include "rfc1918_parser.h"
 
@@ -65,7 +66,8 @@ MODULE_VERSION
 /*
  * Module internal functions
  */
-static int _compare_ips(char*, size_t, enum enum_ip_type, char*, size_t, enum enum_ip_type);
+int _compare_ips(char*, size_t, enum enum_ip_type, char*, size_t, enum enum_ip_type);
+int _ip_is_in_subnet(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type, int netmask);
 
 
 /*
@@ -80,6 +82,7 @@ static int w_ip_type(struct sip_msg*, char*);
 static int w_compare_ips(struct sip_msg*, char*, char*);
 static int w_compare_pure_ips(struct sip_msg*, char*, char*);
 static int w_is_ip_rfc1918(struct sip_msg*, char*);
+static int w_ip_is_in_subnet(struct sip_msg*, char*, char*);
 
 
 /*
@@ -105,6 +108,9 @@ static cmd_export_t cmds[] =
   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
   { "is_ip_rfc1918", (cmd_function)w_is_ip_rfc1918, 1, fixup_spve_null, 0,
   REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
+  { "is_in_subnet", (cmd_function)w_ip_is_in_subnet, 2, fixup_spve_null, 0,
+  REQUEST_ROUTE|FAILURE_ROUTE|ONREPLY_ROUTE|BRANCH_ROUTE|LOCAL_ROUTE },
+  { "bind_ipops", (cmd_function)bind_ipops, 0, 0, 0, 0},
   { 0, 0, 0, 0, 0, 0 }
 };
 
@@ -133,7 +139,7 @@ struct module_exports exports = {
  */
 
 /*! \brief Return 1 if both pure IP's are equal, 0 otherwise. */
-static int _compare_ips(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type)
+int _compare_ips(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type)
 {
   struct in_addr in_addr1, in_addr2;
   struct in6_addr in6_addr1, in6_addr2;
@@ -173,6 +179,61 @@ static int _compare_ips(char *ip1, size_t len1, enum enum_ip_type ip1_type, char
   }
 }
 
+/*! \brief Return 1 if IP1 is in the subnet given by IP2 and the netmask, 0 otherwise. */
+int _ip_is_in_subnet(char *ip1, size_t len1, enum enum_ip_type ip1_type, char *ip2, size_t len2, enum enum_ip_type ip2_type, int netmask)
+{
+  struct in_addr in_addr1, in_addr2;
+  struct in6_addr in6_addr1, in6_addr2;
+  char _ip1[INET6_ADDRSTRLEN], _ip2[INET6_ADDRSTRLEN];
+  uint32_t ipv4_mask;
+  uint8_t ipv6_mask[16];
+  int i;
+  
+  // Not same IP type, return false.
+  if (ip1_type != ip2_type)
+    return 0;
+
+  memcpy(_ip1, ip1, len1);
+  _ip1[len1] = '\0';
+  memcpy(_ip2, ip2, len2);
+  _ip2[len2] = '\0';
+
+  switch(ip1_type) {
+    // Comparing IPv4 with IPv4.
+    case(ip_type_ipv4):
+      if (inet_pton(AF_INET, _ip1, &in_addr1) == 0)  return 0;
+      if (inet_pton(AF_INET, _ip2, &in_addr2) == 0)  return 0;
+      if (netmask <0 || netmask > 32)  return 0;
+      if (netmask == 32) ipv4_mask = 0xFFFFFFFF;
+      else ipv4_mask = htonl(~(0xFFFFFFFF >> netmask));
+      if ((in_addr1.s_addr & ipv4_mask) == in_addr2.s_addr)
+        return 1;
+      else
+        return 0;
+      break;
+    // Comparing IPv6 with IPv6.
+    case(ip_type_ipv6):
+      if (inet_pton(AF_INET6, _ip1, &in6_addr1) != 1)  return 0;
+      if (inet_pton(AF_INET6, _ip2, &in6_addr2) != 1)  return 0;
+      if (netmask <0 || netmask > 128)  return 0;
+      for (i=0; i<16; i++)
+      {
+        if (netmask > ((i+1)*8)) ipv6_mask[i] = 0xFF;
+        else if (netmask > (i*8))  ipv6_mask[i] = ~(0xFF >> (netmask-(i*8)));
+	else ipv6_mask[i] = 0x00;
+      }
+      for (i=0; i<16; i++)  in6_addr1.s6_addr[i] &= ipv6_mask[i];
+      if (memcmp(in6_addr1.s6_addr, in6_addr2.s6_addr, sizeof(in6_addr1.s6_addr)) == 0)
+        return 1;
+      else
+        return 0;
+      break;
+    default:
+      return 0;
+      break;
+  }
+}
+
 
 
 /*
@@ -440,6 +501,80 @@ static int w_compare_pure_ips(struct sip_msg* _msg, char* _s1, char* _s2)
 }
 
 
+/*! \brief Return true if the first IP (string or pv) is within the subnet defined by the second IP in CIDR notation. IPv6 references not allowed. */
+static int w_ip_is_in_subnet(struct sip_msg* _msg, char* _s1, char* _s2)
+{
+  str string1, string2;
+  enum enum_ip_type ip1_type, ip2_type;
+  char *cidr_pos = NULL;
+  int netmask = 0;
+  
+  if (_s1 == NULL || _s2 == NULL ) {
+    LM_ERR("bad parameters\n");
+    return -2;
+  }
+  
+  if (fixup_get_svalue(_msg, (gparam_p)_s1, &string1))
+  {
+    LM_ERR("cannot print the format for first string\n");
+    return -3;
+  }
+  
+  if (fixup_get_svalue(_msg, (gparam_p)_s2, &string2))
+  {
+    LM_ERR("cannot print the format for second string\n");
+    return -3;
+  }
+
+  switch(ip1_type = ip_parser_execute(string1.s, string1.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      return -1;
+      break;
+    default:
+      break;
+  }
+  cidr_pos = string2.s + string2.len - 1;
+  while (cidr_pos > string2.s)
+  {
+    if (*cidr_pos == '/')
+    {
+      string2.len = (cidr_pos - string2.s);
+      netmask = atoi(cidr_pos+1);
+      break;
+    }
+    cidr_pos--;
+  }
+  switch(ip2_type = ip_parser_execute(string2.s, string2.len)) {
+    case(ip_type_error):
+      return -1;
+      break;
+    case(ip_type_ipv6_reference):
+      return -1;
+      break;
+    default:
+      break;
+  }
+
+  if (netmask == 0)
+  {
+    if (_compare_ips(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type))
+      return 1;
+    else
+      return -1;
+  }
+  else
+  {
+    if (_ip_is_in_subnet(string1.s, string1.len, ip1_type, string2.s, string2.len, ip2_type, netmask))
+      return 1;
+    else
+      return -1;
+  }
+}
+
+
 /*! \brief Return true if the given argument (string or pv) is a valid RFC 1918 IPv4 (private address). */
 static int w_is_ip_rfc1918(struct sip_msg* _msg, char* _s)
 {