2
0
Эх сурвалжийг харах

- added ipsets manipulable using RPC
- new modparam "declare_ipset"
- RPC functions: ipset.clean, ipset.add, ipset.commit, ipset.list, ipset.print
- locking "one-writer(RPC)/more readers(script)" implemented
- missing stuff from README added into docbook

Tomas Mandys 17 жил өмнө
parent
commit
3d2616aeb7

+ 210 - 4
modules_s/permissions/doc/functions.xml

@@ -243,7 +243,203 @@ if (method=="REGISTER") {
 	    </programlisting>
 	</example>
     </section>
-    <section id="ip_is_trusted">
+
+    <section id="allow_refer_to(allow_file, deny_file)">
+	<title>
+	    <function>allow_refer_to(allow_file, deny_file)</function>
+	</title>
+	<para>
+		The function returns true if all pairs constructed as
+		described in Section 1.1.2 have appropriate permissions
+		according to the configuration files given as parameters.
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>allow_file</emphasis> -  File containing allow rules.
+		</para>
+		<para>
+		    If the parameter doesn't contain full pathname then the
+		    function expects the file to be located in the same
+		    directory as the main configuration file of the server.
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>deny_file</emphasis> -  File containing deny rules.
+		</para>
+		<para>
+		    If the parameter doesn't contain full pathname then the
+		    function expects the file to be located in the same
+		    directory as the main configuration file of the server.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<example>
+	    <title><function>allow_register(allow_file, deny_file)</function> usage</title>
+	    <programlisting>
+...
+if (method=="REFER") {
+    if (allow_register("refer.allow", "refer.deny")) {
+       ...
+    } else {
+        sl_send_reply("403", "Forbidden");
+    };
+};
+...
+	    </programlisting>
+	</example>
+    </section>
+
+    <section id="ipmatch (string/AVP/select, [avp])">
+	<title>
+	    <function>ipmatch (string/AVP/select, [avp])</function>
+	</title>
+	<para>
+		The function tries to find an IP address and port pair
+		(defined by the first function parameter) in the cached
+		database table. Port is optional, it is compared only if
+		both the function parameter and the database entry contain it. 
+	</para>
+	<para>Meaning of the parameters is as follows:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>string/AVP/select</emphasis> -  File containing allow rules.
+		</para>
+		<itemizedlist>
+			<listitem>
+				<para><emphasis>string</emphasis>:</para>
+				<para>
+				"src": the source address of the packet is used
+		        "via2": the ip address of the 2nd via line is used
+		        other values are not defined currently
+				</para>
+			</listitem>
+			<listitem>
+				<para><emphasis>AVP</emphasis>:</para>				
+				<para>
+					e.g. "$myavp"
+				</para>
+			</listitem>
+			<listitem>
+				<para><emphasis>select call</emphasis>:</para>				
+				<para>
+					e.g. "@via[0].host"
+				</para>
+			</listitem>
+		</itemizedlist>
+	    </listitem>
+	    <listitem>
+		<para>
+			The second parameter is optional, it is used to set an AVP
+			value from the database. Suitable for assigning logical
+			identifiers to gateways.
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<para>
+		Note that IPv6 addresses must be enclosed in square brackets
+		in case of port is defined: [1111:2222::3333]:5060
+	</para>
+    </section>
+
+    <section id="ipmatch_onsend(string)">
+	<title>
+	    <function>ipmatch_onsend (string)</function>
+	</title>
+	<para>
+		<function>ipmatch()</function> function replacement for <emphasis>onsend_route</emphasis> block.
+	</para>
+	<para>
+		The function accepts only string parameter, because even AVP
+		reading is unsafe in onsend_route.
+	</para>
+		  
+	<para>Meaning of the parameter:</para>
+	<itemizedlist>
+	    <listitem>
+		<para><emphasis>"dst"</emphasis>:		
+		</para>
+		<para>
+		the destination address is used
+		</para>
+	    </listitem>
+	    <listitem>
+		<para><emphasis>"ruri"</emphasis>:		
+		</para>
+		<para>
+		the ip:port pair is extracted from the Request URI
+		</para>
+	    </listitem>
+	</itemizedlist>
+	<para>
+		The function can be used for example to catch unauthorized requests
+		going to gateways: Use a flag to mark the call as PSTN either
+		in request or in failure_route, and do not touch it in onreply_route.
+		See the examples below.
+	</para>
+    </section>
+
+    <section id="ipmatch_filter (unsigned int)">
+	<title>
+	    <function>ipmatch_filter (unsigned int)</function>
+	</title>
+	<para>
+		Entries in the database can be marked to group the different kind
+		of network elements. The function sets the filter which is used on
+		the mark while comparing the IP addresses. The mark must be the
+		power of 2 in the database!
+	</para>
+	<para>
+		Note that ipmatch() and ipmatch_onsend() functions reset the filter!
+	</para>
+		  
+	<example>
+	    <title><function>ipmatch_filter (unsigned int)</function> usage</title>
+	    <programlisting>
++-----------------+-------------------+------+------+
+| ip              | avp_val           | mark | flag |
++-----------------+-------------------+------+------+
+| 1111:2222::1001 | first_gw          |    1 |    1 |
+| 10.38.2.10:5060 | second_gw         |    1 |    1 |
+| 10.0.0.10       | first_peering     |    2 |    1 |
++-----------------+-------------------+------+------+
+
+
+route[0] {
+  # is this a request from a GW?
+  ipmatch_filter("1");
+  if (ipmatch("src", "$gw_id")) {
+    # yes, it is from a GW
+    ...
+  } else {
+    # is this a request from a peering partner?
+    ipmatch_filter("2");
+    if (ipmatch("src", "$peering_id")) {
+       # yes, it is from a peering partner
+       ...
+    };
+  }
+  ...
+  # request goes to PSTN
+  setflag(PSTN);
+}
+
+onsend_route[0] {
+  if (method == "INVITE" &amp;&amp; (!isflagset(PSTN))) {
+     # is this a request to a GW?
+     ipmatch_filter("1");
+     if (ipmatch_onsend("dst")) {
+        # request is not marked with PSTN flag, but it goes to a gateway
+        drop;
+     }
+  }
+  ...
+}
+	    </programlisting>
+	</example>
+    </section>
+
+	<section id="ip_is_trusted">
 	<title>
 	    <function>ip_is_trusted(ip_set, ip)</function>
 	</title>
@@ -257,10 +453,14 @@ if (method=="REGISTER") {
 		<para>
 			<emphasis>ip_set</emphasis> is identified by comma/space/semicolon
 			delimited list of IP addresses or subnet masks. The subnet mask
-			is written in IP_slash_number form. If the number is not mentioned 
-			then default value is taken, the default  value is number of bits
+			is written in IP_slash_number form or as IP. If the mask is not mentioned 
+			then default value is taken, the default value is number of bits
 			of particular IP address, i.e. 32 in case of IPv4, 128 in case of IPv6.
 		</para>
+		<para>
+			Besides list also ip set identifier declared by <varname>declare_ipset</varname> may
+			be provided. In this case ip is checked against set maintainded by RPC commands.
+		</para>
 	    </listitem>
 		<listitem>
 		<para>
@@ -274,6 +474,9 @@ if (method=="REGISTER") {
 	<example>
 	    <title><function>ip_is_trusted</function> usage</title>
 	    <programlisting>
+modparam("permissions", "declare_ipset", "my_ipset1");
+modparam("permissions", "declare_ipset", "my_ipset2");
+	
 route[TT2] {
 	if (ip_is_trusted("$net", "$ip")) {
 		xlog("L_E", "'%$ip' - TRUE\n");
@@ -338,7 +541,7 @@ route[TEST] {
 	$net = "255.255.255.255/0"; # all IPv4 addresses, dentical to 0.0.0.0/0
 	route(TT1);
 	
-	$net = "127.0.0.1/24";
+	$net = "127.0.0.1/255.255.255.0";
 	route(TT1);
 	
 	$net = "10.0.0.0/8";  # All type A addresses
@@ -365,7 +568,10 @@ route[TEST] {
 	$net = "[0:2:4:A:B:D:E:f300]/120";
 	route(TT1);
 
+	$net = "my_ipset1";
+	route(TT1);
 
+}
 # the result is:
 
 Testing netmask '0.0.0.0 128.2.3.4/1 127.0.128.16 [0:2:4:A:B:D:E:F301]'

+ 9 - 0
modules_s/permissions/doc/params.xml

@@ -186,4 +186,13 @@ modparam("permissions", "deny_suffix", ".deny")
 		Default value is "ipmatch".
 	</para>
     </section>
+    
+    <section id="declare_ipset">
+	<title><varname>declare_ipset</varname> (string)</title>
+	<para>
+		Declares name of ip set which can be manipulated via RPC commands and 
+		tested using <function>ip_is_trusted</function>.
+		Identifier must start with letter or underscore.
+	</para>
+    </section>
 </section>

+ 3 - 2
modules_s/permissions/doc/permissions.xml

@@ -163,8 +163,8 @@
 		same as described in Section 1.1.1.
 	    </para>
 	</section>
-	<section id="refer_to_permissions">
-	    <title>Refer-To Permissions</title>
+	<section id="Messages catching">
+	    <title>Messages catching</title>
 	    <para>
 		The module can be also used for catching messages coming from
 		or going to specific network elements, for example gateways or
@@ -180,5 +180,6 @@
     
     <xi:include href="params.xml"/>
     <xi:include href="functions.xml"/>
+    <xi:include href="xmlrpc.xml"/>
 
 </section>

+ 64 - 0
modules_s/permissions/doc/xmlrpc.xml

@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<section id="permissions.xmlrpc" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <sectioninfo>
+	<revhistory>
+	    <revision>
+		<revnumber>$Revision$</revnumber>
+		<date>$Date$</date>
+	    </revision>
+	</revhistory>
+    </sectioninfo>
+
+    <title>XMLRPC Interface</title>
+
+    <para>
+	Some functionality may be controled using RPC commands.
+    </para>
+    <itemizedlist>
+	<listitem>
+	    <para>
+		<emphasis>ipmatch.reload</emphasis> - 
+			Reloads the cached ipmatch table. The original table remains
+			active in case of any failure.
+	    </para>
+	</listitem>
+	<listitem>
+	    <para>
+		<emphasis>ipset.clean(ipset_name)</emphasis> - Clear
+			all entries in "pending" ipset. 
+	    </para>
+	</listitem>
+	<listitem>
+	    <para>
+		<emphasis>ipset.add(ipset_name, ip, netmask)</emphasis> - Add
+			ip and mask into ipset. IPv6 should should be enclosed 
+			in brackets. Netmask may be identified as number or in IP form.
+			Note that number requires leading slash, e.g. "/24" or "255.255.255.0".
+	    </para>
+	</listitem>
+	<listitem>
+	    <para>
+		<emphasis>ipset.commit(ipset_name)</emphasis> - Makes
+			pending ip set usable by <function>ip_is_trusted</function>.
+			Pending ip set is cleared.
+	    </para>
+	</listitem>
+	<listitem>
+	    <para>
+		<emphasis>ipset.list()</emphasis> - List
+			declared ip sets.
+	    </para>
+	</listitem>
+	<listitem>
+	    <para>
+		<emphasis>ipset.print(ipset_name, pending)</emphasis> - Dump
+			ipset trees. If <varname>pending</varname> non zero then
+			pending ipset is dumped.
+	    </para>
+	</listitem>
+	
+    </itemizedlist>
+</section>

+ 108 - 47
modules_s/permissions/ip_set.c

@@ -30,8 +30,9 @@
 #include <stdio.h>
 #include <string.h>
 
-void ip_set_init(struct ip_set *ip_set) {
+void ip_set_init(struct ip_set *ip_set, int use_shm) {
 	memset(ip_set, 0, sizeof(*ip_set));
+	ip_set->use_shm = use_shm;
 	ip_tree_init(&ip_set->ipv4_tree);
 	#ifdef USE_IPV6
 	ip_tree_init(&ip_set->ipv6_tree);
@@ -39,19 +40,19 @@ void ip_set_init(struct ip_set *ip_set) {
 }
 
 void ip_set_destroy(struct ip_set *ip_set) {
-	ip_tree_destroy(&ip_set->ipv4_tree, 0);
+	ip_tree_destroy(&ip_set->ipv4_tree, 0, ip_set->use_shm);
 	#ifdef USE_IPV6
-	ip_tree_destroy(&ip_set->ipv6_tree, 0);
+	ip_tree_destroy(&ip_set->ipv6_tree, 0, ip_set->use_shm);
 	#endif
 }
 
 int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix) {
 	switch (ip->af) {
 		case AF_INET:
-			return ip_tree_add_ip(&ip_set->ipv4_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix);
+			return ip_tree_add_ip(&ip_set->ipv4_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix, ip_set->use_shm);
 	#ifdef USE_IPV6
 		case AF_INET6:
-			return ip_tree_add_ip(&ip_set->ipv6_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix);
+			return ip_tree_add_ip(&ip_set->ipv6_tree, ip->u.addr, (ip->len*8<network_prefix)?ip->len*8:network_prefix, ip_set->use_shm);
 	#endif
 		default:
 			return -1;				
@@ -81,62 +82,122 @@ void ip_set_print(FILE *stream, struct ip_set *ip_set) {
 	#endif
 }
 
-int ip_set_parse(struct ip_set *ip_set, str ip_set_s){
+int ip_set_add_list(struct ip_set *ip_set, str ip_set_s){
 
-	str s;
-	struct ip_addr *ip;
-	unsigned int prefix;
-	
-	ip_set_init(ip_set);
-
-	/* parse comma delimited string of IPs and masks,  e.g. 1.2.3.4,2.3.5.3/24,[abcd:12456:2775:ab::7533] */
+	str ip_s, mask_s;
+				 
+	/* parse comma delimited string of IPs and masks,  e.g. 1.2.3.4,2.3.5.3/24,[abcd:12456:2775:ab::7533],9.8.7.6/255.255.255.0 */
 	while (ip_set_s.len) {
-		while (ip_set_s.len && (*ip_set_s.s == ',' || *ip_set_s.s == ';' || *ip_set_s.s == ' ')) {			
+		while (ip_set_s.len && (*ip_set_s.s == ',' || *ip_set_s.s == ';' || *ip_set_s.s == ' ')) {
 			ip_set_s.s++;
 			ip_set_s.len--;
 		}
-		s.s = ip_set_s.s;
-		s.len = 0;
-		while (s.len < ip_set_s.len && (s.s[s.len] != ',' && s.s[s.len] != ';' && s.s[s.len] != ' ' && s.s[s.len] != '/')) {
-			s.len++;
-		}
-		if ( ((ip = str2ip(&s))==0)
-			#ifdef  USE_IPV6
-						  && ((ip = str2ip6(&s))==0)
-			#endif
-										  ){
-			ip_set_destroy(ip_set);
-			ERR("ip_set_parse: string to ip conversion error near '%.*s'\n", ip_set_s.len, ip_set_s.s);
-			return -1;
+		ip_s.s = ip_set_s.s;
+		ip_s.len = 0;
+		while (ip_s.len < ip_set_s.len && (ip_s.s[ip_s.len] != ',' && ip_s.s[ip_s.len] != ';' && ip_s.s[ip_s.len] != ' ' && ip_s.s[ip_s.len] != '/')) {
+			ip_s.len++;
 		}
-		ip_set_s.s += s.len;
-		ip_set_s.len -= s.len;
+		ip_set_s.s += ip_s.len;
+		ip_set_s.len -= ip_s.len;
+		mask_s.len = 0;
 		if (ip_set_s.len && ip_set_s.s[0] == '/') {
 			ip_set_s.s++;
 			ip_set_s.len--;
-			s.s = ip_set_s.s;
-			s.len = 0;
-			while (s.len < ip_set_s.len && (s.s[s.len] >= '0' && s.s[s.len] <= '9')) {
-				s.len++;
+			mask_s.s = ip_set_s.s;
+			while (mask_s.len < ip_set_s.len && (mask_s.s[mask_s.len] != ',' && mask_s.s[mask_s.len] != ';' && mask_s.s[mask_s.len] != ' ')) {
+				mask_s.len++;
 			}
-			if (str2int(&s, &prefix) < 0) {
-				ERR("ip_set_parse: cannot convert mask near '%.*s'\n", ip_set_s.len, ip_set_s.s);
-				ip_set_destroy(ip_set);
+			ip_set_s.s += mask_s.len;
+			ip_set_s.len -= mask_s.len;
+		}
+		if (ip_set_add_ip_s(ip_set, ip_s, mask_s) < 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int ip_set_add_ip_s(struct ip_set *ip_set, str ip_s, str mask_s){
+
+	int fl;
+	struct ip_addr *ip, ip_buff;
+	unsigned int prefix, i;
+
+	if ( ((ip = str2ip(&ip_s))==0)
+		#ifdef  USE_IPV6
+					  && ((ip = str2ip6(&ip_s))==0)
+		#endif
+									  ){
+		ERR("ip_set_add_ip_s: string to ip conversion error '%.*s'\n", ip_s.len, ip_s.s);
+		return -1;
+	}
+	ip_buff = *ip;
+
+	if (mask_s.len > 0) {
+		i = 0;
+		fl = 0;
+		while (i < mask_s.len &&
+			   ((mask_s.s[i] >= '0' && mask_s.s[i] <= '9') || 
+				(mask_s.s[i] >= 'a' && mask_s.s[i] <= 'f') || 
+				(mask_s.s[i] >= 'A' && mask_s.s[i] <= 'F') ||
+				mask_s.s[i] == '.' ||
+				mask_s.s[i] == ':' ||
+				mask_s.s[i] == '[' ||
+				mask_s.s[i] == ']'
+				) ) {
+			fl |= mask_s.s[i] < '0' || mask_s.s[i] > '9';
+			i++;
+		}
+		
+		if (fl) {  /* 255.255.255.0 format */
+			if ( ((ip = str2ip(&mask_s))==0)
+				#ifdef  USE_IPV6
+					  && ((ip = str2ip6(&mask_s))==0)
+				#endif
+				  ){
+				ERR("ip_set_add_ip_s: string to ip mask conversion error '%.*s'\n", mask_s.len, mask_s.s);
+				return -1;
+			}
+			if (ip_buff.af != ip->af) {
+				ERR("ip_set_add_ip_s: IPv4 vs. IPv6 near '%.*s' vs. '%.*s'\n", ip_s.len, ip_s.s, mask_s.len, mask_s.s);
+				return -1;
+			}
+			fl = 0;
+			prefix = 0;
+			for (i=0; i<ip->len; i++) {				
+				unsigned char msk;				
+				msk = 0x80;
+				while (msk) {
+					if ((ip->u.addr[i] & msk) != 0) {
+						if (fl) {
+							ERR("ip_set_add_ip_s: bad IP mask '%.*s'\n", mask_s.len, mask_s.s);
+							return -1;
+						}
+						prefix++;
+					}
+					else {
+						fl = 1;
+					}
+					msk /= 2;
+				}
+			}
+		}	
+		else {     /* 24 format */
+			if (str2int(&mask_s, &prefix) < 0) {
+				ERR("ip_set_add_ip_s: cannot convert mask '%.*s'\n", mask_s.len, mask_s.s);
 				return -1;
 			}				
-			ip_set_s.s += s.len;
-			ip_set_s.len -= s.len;
-		}
-		else {
-			prefix = ip->len*8;
 		}
-		if (ip_set_add_ip(ip_set, ip, prefix) < 0) {
-			ERR("ip_set_parse: cannot add IP into ip set\n");
-			ip_set_destroy(ip_set);
-			return -1;
-		}		
 	}
-	return 0;		
+	else {
+		prefix = ip_buff.len*8;
+	}
+	if (ip_set_add_ip(ip_set, &ip_buff, prefix) < 0) {
+		ERR("ip_set_add_ip_s: cannot add IP into ip set\n");
+		return -1;
+	}		
+	return 0;
 }
 
 
+

+ 4 - 2
modules_s/permissions/ip_set.h

@@ -35,17 +35,19 @@
 /* ip_set stuff, combines IPv4 and IPv6 tree in one set */
    
 struct ip_set {
+	int use_shm;
 	struct ip_tree_leaf *ipv4_tree;
 #ifdef USE_IPV6
 	struct ip_tree_leaf *ipv6_tree;	
 #endif
 };
 
-extern void ip_set_init(struct ip_set *ip_set);
+extern void ip_set_init(struct ip_set *ip_set, int use_shm);
 extern void ip_set_destroy(struct ip_set *ip_set);
 extern int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix);
+extern int ip_set_add_ip_s(struct ip_set *ip_set, str ip_s, str mask_s);
 extern int ip_set_ip_exists(struct ip_set *ip_set, struct ip_addr *ip);
 extern void ip_set_print(FILE *stream, struct ip_set *ip_set);
-extern int ip_set_parse(struct ip_set *ip_set, str ip_set_s);
+extern int ip_set_add_list(struct ip_set *ip_set, str ip_set_s);
 
 #endif

+ 274 - 0
modules_s/permissions/ip_set_rpc.c

@@ -0,0 +1,274 @@
+/* 
+ * $Id$
+ *
+ * allow_trusted related functions
+ *
+ * Copyright (C) 2003 Juha Heinanen
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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
+ *
+ * History:
+ * --------
+ *   2008-08-01: released
+ */
+
+#include "../../dprint.h"
+#include "../../db/db.h"
+#include "../../mem/shm_mem.h"
+#include "permissions.h"
+#include "ip_set.h"
+#include "ip_set_rpc.h"
+
+
+static struct ip_set_list_item *ip_set_list = NULL;
+static int ip_set_list_count = 0;
+
+int ip_set_list_malloc(int num, str* names) {
+	int i;
+	if (num) {
+		ip_set_list = shm_malloc(num*sizeof(*ip_set_list));
+		if (!ip_set_list) return -1;
+		ip_set_list_count = num;
+		for (i=0; i<ip_set_list_count; i++) {
+			ip_set_list[i].idx = i;
+			ip_set_list[i].name = names[i];
+			lock_init(&ip_set_list[i].read_lock);
+			lock_init(&ip_set_list[i].write_lock);
+			ip_set_list[i].ip_set = NULL;
+			ip_set_init(&ip_set_list[i].ip_set_pending, 1);
+		}
+	}
+	return 0;
+}
+
+void ip_set_list_free() {
+	int i;
+	if (!ip_set_list) return;
+	for (i=0; i<ip_set_list_count; i++) {
+		lock_destroy(&ip_set_list[i].read_lock);
+		lock_destroy(&ip_set_list[i].write_lock);
+		if (ip_set_list[i].ip_set) {
+			if (atomic_dec_and_test(&ip_set_list[i].ip_set->refcnt)) { /* do not destroy cloned sets because if they can live only in local copy after commit,
+										   they must be deleted separately in local copy before this procedure is called*/
+				ip_set_destroy(&ip_set_list[i].ip_set->ip_set);
+				shm_free(ip_set_list[i].ip_set);
+			}			
+		}
+		ip_set_destroy(&ip_set_list[i].ip_set_pending);
+	}
+	shm_free(ip_set_list);
+	ip_set_list = NULL;
+
+}
+
+struct ip_set_list_item* ip_set_list_find_by_name(str name) {
+	int i;
+	for (i=0; i<ip_set_list_count; i++) {
+		if (ip_set_list[i].name.len == name.len && memcmp(ip_set_list[i].name.s, name.s, name.len) == 0)
+			return &ip_set_list[i];
+	}
+	return NULL;
+}
+
+
+const char* rpc_ip_set_clean_doc[] = {
+	"Clean ip set.",
+	0
+};
+
+
+void rpc_ip_set_clean(rpc_t* rpc, void* ctx) {
+	str name;
+	struct ip_set_list_item *p;
+	if (rpc->scan(ctx, "S", &name) < 1) {
+		return;
+	}
+	p = ip_set_list_find_by_name(name);
+	if (!p) {
+		rpc->fault(ctx, 400, "Ip set not found");
+		return;
+	}
+	lock_get(&p->write_lock);
+	ip_set_destroy(&p->ip_set_pending);
+	ip_set_init(&p->ip_set_pending, 1);
+	lock_release(&p->write_lock);
+}
+
+const char* rpc_ip_set_add_doc[] = {
+	"Add IP/mask in ip set.",
+	0
+};
+
+
+void rpc_ip_set_add(rpc_t* rpc, void* ctx) {
+	str name, ip, mask;
+	struct ip_set_list_item* p;
+	if (rpc->scan(ctx, "SSS", &name, &ip, &mask) < 1) {
+		return;
+	}
+	while (mask.len && mask.s[0]=='/') {  /* rpc cannot read plain number as string, adding '/' helps */
+		mask.s++;
+		mask.len--;
+	}
+	p = ip_set_list_find_by_name(name);
+	if (!p) {
+		rpc->fault(ctx, 400, "Ip set not found");
+		return;
+	}
+	lock_get(&p->write_lock);
+	if (ip_set_add_ip_s(&p->ip_set_pending, ip, mask) < 0) {
+		lock_release(&p->write_lock);
+		rpc->fault(ctx, 400, "Cannot add ip/mask into ip set");
+		return;
+	}
+	lock_release(&p->write_lock);
+}
+
+const char* rpc_ip_set_commit_doc[] = {
+	"Commit changes in ip set.",
+	0
+};
+
+void rpc_ip_set_commit(rpc_t* rpc, void* ctx) {
+	str name;
+	struct ip_set_list_item *p;
+	struct ip_set_ref *new_ip_set;
+	if (rpc->scan(ctx, "S", &name) < 1) {
+		return;
+	}
+	p = ip_set_list_find_by_name(name);
+	if (!p) {
+		rpc->fault(ctx, 400, "Ip set not found");
+		return;
+	}
+	lock_get(&p->write_lock);
+	lock_get(&p->read_lock);
+	new_ip_set = shm_malloc(sizeof(*new_ip_set));
+	if (!new_ip_set) {
+		rpc->fault(ctx, 500, "Not enough memory");
+		return;
+	}
+	
+	if (p->ip_set) {
+		if (atomic_dec_and_test(&p->ip_set->refcnt)) {
+			ip_set_destroy(&p->ip_set->ip_set);  /* not used in local copy */
+			shm_free(p->ip_set);
+		}
+	}
+	new_ip_set->ip_set = p->ip_set_pending;
+	atomic_set(&new_ip_set->refcnt, 1);
+	p->ip_set = new_ip_set;
+	
+	ip_set_init(&p->ip_set_pending, 1);
+	lock_release(&p->read_lock);
+	lock_release(&p->write_lock);
+}
+
+const char* rpc_ip_set_list_doc[] = {
+	"List ip set names.",
+	0
+};
+
+
+void rpc_ip_set_list(rpc_t* rpc, void* ctx) {
+	int i;
+	void *c;
+	rpc->add(ctx, "{", &c);
+	for (i=0; i<ip_set_list_count; i++) {
+		if (rpc->struct_add(c, "S", "Name", &ip_set_list[i].name) < 0) {
+			 rpc->fault(ctx, 500, "Error when listing ip sets");
+		}
+	}
+}
+
+const char* rpc_ip_set_print_doc[] = {
+	"Print ip set.",
+	0
+};
+
+
+static int rpc_ip_tree_print(rpc_t* rpc, void *ctx, char *prefix, struct ip_tree_leaf *tree, unsigned int indent) {
+	int j;
+	if (!tree) {
+		if (rpc->struct_printf(ctx, "", "%*snil", indent, prefix) < 0) return -1;
+	}
+	else {
+		str s;
+		s = ip_tree_mask_to_str(tree->prefix_match, tree->prefix_match_len);
+		if (rpc->struct_printf(ctx, "", "%*smatch %d bits {%.*s}", indent, prefix, tree->prefix_match_len, s.len, s.s) < 0) 
+			return -1;
+		for (j=0; j<=1; j++) {
+			if (rpc_ip_tree_print(rpc, ctx, j==0?"0:":"1:", tree->next[j], indent+2) < 0)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+	
+void rpc_ip_set_print(rpc_t* rpc, void* ctx) {
+	struct ip_set_list_item *p;
+	struct ip_set *ip_set, ip_set2;
+	void *c;
+	str name;
+	int pending;
+	if (rpc->scan(ctx, "Sd", &name, &pending) < 1) {
+		return;
+	}
+	p = ip_set_list_find_by_name(name);
+	if (!p) {
+		rpc->fault(ctx, 400, "Ip set not found");
+		return;
+	}
+	if (pending) {
+		lock_get(&p->write_lock);
+		ip_set = &p->ip_set_pending;
+	} else {
+		lock_get(&p->read_lock);
+		if (!p->ip_set) {
+			ip_set_init(&ip_set2, 1); /* dummy to return empty ip set */
+			ip_set = &ip_set2;
+		}
+		else
+			ip_set = &p->ip_set->ip_set;
+	}
+
+	/* nested array/struct not supported */
+	rpc->add(ctx, "{", &c);
+	if (rpc->struct_add(c, "s", "IPv", "4") < 0) 
+		goto err;	
+	if (rpc_ip_tree_print(rpc, c, "", ip_set->ipv4_tree, 0) < 0) 
+		goto err;
+	rpc->add(ctx, "{", &c);
+	if (rpc->struct_add(c, "s", "IPv", "6") < 0) 
+		goto err;	
+	if (rpc_ip_tree_print(rpc, c, "", ip_set->ipv6_tree, 0) < 0) 
+		goto err;
+
+err:		
+	if (pending)
+		lock_release(&p->write_lock);
+	else
+		lock_release(&p->read_lock);
+
+}
+

+ 73 - 0
modules_s/permissions/ip_set_rpc.h

@@ -0,0 +1,73 @@
+/* 
+ * $Id$
+ *
+ * allow_trusted related functions
+ *
+ * Copyright (C) 2008 iptelorg GmbH
+ *
+ * This file is part of ser, a free SIP server.
+ *
+ * ser 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
+ *
+ * For a license to use the ser software under conditions
+ * other than those described here, or to purchase support for this
+ * software, please contact iptel.org by e-mail at the following addresses:
+ *    [email protected]
+ *
+ * ser 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 _IP_SET_RPC_H
+#define _IP_SET_RPC_H 1
+
+#include "ip_set.h"
+#include "../../atomic_ops.h"
+#include "../../lock_ops.h"
+#include "../../rpc.h"
+
+struct ip_set_ref {
+	struct ip_set ip_set;
+	atomic_t refcnt;
+};
+
+struct ip_set_list_item {
+	int idx;
+	str name;
+	gen_lock_t read_lock;
+	gen_lock_t write_lock;
+	struct ip_set_ref *ip_set;
+	struct ip_set ip_set_pending;
+};
+	    
+extern int ip_set_list_malloc(int num, str *names);
+extern void ip_set_list_free();
+extern struct ip_set_list_item* ip_set_list_find_by_name(str name);
+
+/* RPC functions */
+
+extern const char* rpc_ip_set_clean_doc[];
+extern void rpc_ip_set_clean(rpc_t* rpc, void* ctx);
+
+extern const char* rpc_ip_set_add_doc[];
+extern void rpc_ip_set_add(rpc_t* rpc, void* ctx);
+
+extern const char* rpc_ip_set_commit_doc[];
+extern void rpc_ip_set_commit(rpc_t* rpc, void* ctx);
+
+extern const char* rpc_ip_set_list_doc[];
+extern void rpc_ip_set_list(rpc_t* rpc, void* ctx);
+
+extern const char* rpc_ip_set_print_doc[];
+extern void rpc_ip_set_print(rpc_t* rpc, void* ctx);
+
+#endif /* _IP_SET_RPC_H */

+ 47 - 24
modules_s/permissions/ip_tree.c

@@ -27,6 +27,7 @@
 
 #include "ip_tree.h"
 #include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
 
 #include <stdio.h>
 #include <string.h>
@@ -35,15 +36,18 @@ void ip_tree_init(struct ip_tree_leaf **tree) {
 	*tree = NULL;
 }
 
-void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only) {
+void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only, int use_shm) {
 	int i;
 	if (*tree) {
 		for (i=0; i<=1; i++) {
 			if ((*tree)->next[i])
-				ip_tree_destroy(&(*tree)->next[i], 0);
+				ip_tree_destroy(&(*tree)->next[i], 0, use_shm);
 		}
 		if (!leaves_only) {
-			pkg_free(*tree);
+			if (use_shm) 
+			    shm_free(*tree);
+			else
+				pkg_free(*tree);
 			*tree = NULL;
 		}
 	}
@@ -104,11 +108,16 @@ int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int i
 	return IP_TREE_FIND_FOUND;
 }
 
-static inline struct ip_tree_leaf *ip_tree_malloc_leaf(unsigned int ip_len) {
-	return pkg_malloc(sizeof(struct ip_tree_leaf)+((ip_len==0)?0:((ip_len-1)/8 +1)));
+static inline struct ip_tree_leaf *ip_tree_malloc_leaf(unsigned int ip_len, int use_shm) {
+	int n;
+	n = sizeof(struct ip_tree_leaf)+((ip_len==0)?0:((ip_len-1)/8 +1));
+	if (use_shm) 
+		return shm_malloc(n);
+	else
+		return pkg_malloc(n);
 }
 
-int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len) {
+int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len, int use_shm) {
 	struct ip_tree_find h;
 	struct ip_tree_leaf *l0, *l1;
 	int ret, i, n;
@@ -119,7 +128,7 @@ int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int i
 		case IP_TREE_FIND_FOUND_UPPER_SET:
 			/* ip covers wider subnet range than already defined range, we can delete all subleaves and reduce prefix match match */
 			h.leaf->prefix_match_len = h.leaf_prefix_match_len;
-			ip_tree_destroy(&h.leaf, 1);
+			ip_tree_destroy(&h.leaf, 1, use_shm);
 			break;
 			
 		case IP_TREE_FIND_FOUND:
@@ -130,15 +139,15 @@ int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int i
 			if (h.leaf) {
 				/* split leaf into two leaves */
 				n = h.ip_len - 1;
-				l1 = ip_tree_malloc_leaf(n);
+				l1 = ip_tree_malloc_leaf(n, use_shm);
 				if (!l1) return -1;
 				l1->prefix_match_len = n;
 				for (i=0; i<=1; i++)
 					l1->next[i] = NULL;				
 				n = h.leaf->prefix_match_len - h.leaf_prefix_match_len - 1;
-				l0 = ip_tree_malloc_leaf(n);
+				l0 = ip_tree_malloc_leaf(n, use_shm);
 				if (!l0) {
-					ip_tree_destroy(&l1, 0);
+					ip_tree_destroy(&l1, 0, use_shm);
 					return -1;
 				}
 				l0->prefix_match_len = n;
@@ -207,7 +216,7 @@ int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int i
 			}
 			else {
 				/* it's first leaf in tree */
-				*tree = ip_tree_malloc_leaf(ip_len);
+				*tree = ip_tree_malloc_leaf(ip_len, use_shm);
 				if (!*tree) return -1;
 				(*tree)->prefix_match_len = ip_len;
 				if (ip_len > 0) {
@@ -226,25 +235,15 @@ int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int i
 }
 
 void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent) {
-	unsigned char mask, *pm;
 	unsigned int i, j;
 
 	if (!tree) {
 		fprintf(stream, "nil\n"); 
 	}
 	else {
-		fprintf(stream, "match %d bits {", tree->prefix_match_len);
-		for (i=0, pm=tree->prefix_match, mask=0x80; i<tree->prefix_match_len; i++) {
-			fprintf(stream, (*pm & mask)?"1":"0");
-			if (mask == 0x01) {
-				mask = 0x80;
-				pm++;
-			}
-			else {
-				mask /= 2;
-			}
-		}
-		fprintf(stream, "}\n");
+		str s;
+		s = ip_tree_mask_to_str(tree->prefix_match, tree->prefix_match_len);
+		fprintf(stream, "match %d bits {%.*s}\n", tree->prefix_match_len, s.len, s.s);
 		for (j=0; j<=1; j++) {
 			for (i=0; i<indent; i++) fprintf(stream, " ");
 			fprintf(stream, "%d:", j);
@@ -252,3 +251,27 @@ void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent)
 		}		
 	}
 }
+
+str ip_tree_mask_to_str(unsigned char *pm, unsigned int len) {
+	unsigned char mask;
+	unsigned int i;
+	static char buf[129];
+	str s;
+	
+	s.s = buf;
+	if (len>=sizeof(buf))
+	    len = sizeof(buf)-1;
+	s.len = len;
+	buf[len] = '\0';
+	for (i=0, mask=0x80; i<len; i++) {
+		buf[i] = (*pm & mask)?'1':'0';
+		if (mask == 0x01) {
+			mask = 0x80;
+			pm++;
+		}
+		else {
+			mask /= 2;
+		}
+	}
+	return s;
+}

+ 3 - 3
modules_s/permissions/ip_tree.h

@@ -106,9 +106,9 @@ struct ip_tree_find {
 #define IP_TREE_FIND_FOUND_UPPER_SET 2
 
 extern void ip_tree_init(struct ip_tree_leaf **tree);
-extern void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only);
+extern void ip_tree_destroy(struct ip_tree_leaf **tree, int leaves_only, int use_shm);
 extern int ip_tree_find_ip(struct ip_tree_leaf *tree, unsigned char *ip, unsigned int ip_len, struct ip_tree_find *h);
-extern int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len);
+extern int ip_tree_add_ip(struct ip_tree_leaf **tree, unsigned char *ip, unsigned int ip_len, int use_shm);
 extern void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent);
-
+extern str ip_tree_mask_to_str(unsigned char *pm, unsigned int len);
 #endif

+ 161 - 35
modules_s/permissions/permissions.c

@@ -36,10 +36,13 @@
  *               safe_file_load module parameter introduced (Miklos)
  *   2006-08-14: child processes do not keep the DB connection open
  *               if cache is enabled (Miklos)
+ *   2008-07-xx: added ip_is_trusted function (tma)
+ *   2008-08-01: added ipset manipulable via RPC (tma)
  */
 
 #include <stdio.h>
 #include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
 #include "permissions.h"
 #include "trusted.h"
 #include "allow_files.h"
@@ -82,12 +85,17 @@ char	*ipmatch_table = "ipmatch";
 /* Database API */
 db_ctx_t	*db_conn = NULL;
 
+static str *ip_set_list_names = NULL;    /* declared names */
+static struct ip_set_ref **ip_set_list_local = NULL;  /* local copy of ip set in shared memory */
+static int ip_set_list_count = 0;  /* number of delared names */
+
 /* fixup function prototypes */
 static int fixup_files_1(void** param, int param_no);
 static int fixup_files_2(void** param, int param_no);
 static int fixup_ip_is_trusted(void** param, int param_no);
 static int fixup_w_im(void **, int);
 static int fixup_w_im_onsend(void **, int);
+static int fixup_param_declare_ip_set( modparam_t type, void* val);
 
 /* module function prototypes */
 static int allow_routing_0(struct sip_msg* msg, char* str1, char* str2);
@@ -125,6 +133,7 @@ static cmd_export_t cmds[] = {
 	{"ipmatch_onsend", w_im_onsend,      1, fixup_w_im_onsend, ONSEND_ROUTE },
 	{"ipmatch_filter", w_im_filter,      1, fixup_int_1,     REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | ONSEND_ROUTE},
 	{"ip_is_trusted",  w_ip_is_trusted,  2, fixup_ip_is_trusted, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | ONSEND_ROUTE | BRANCH_ROUTE},
+	{"ip_is_in_ipset", w_ip_is_trusted,  2, fixup_ip_is_trusted, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | ONSEND_ROUTE | BRANCH_ROUTE},
 	       	       
         {0, 0, 0, 0, 0}
 };
@@ -145,6 +154,8 @@ static param_export_t params[] = {
 	{"proto_col",          PARAM_STRING, &proto_col         },
 	{"from_col",           PARAM_STRING, &from_col          },
 	{"ipmatch_table",      PARAM_STRING, &ipmatch_table     },
+	{"declare_ipset",      PARAM_STRING|PARAM_USE_FUNC, fixup_param_declare_ip_set},
+	
         {0, 0, 0}
 };
 
@@ -272,6 +283,7 @@ static int mod_init(void)
 {
 	LOG(L_INFO, "permissions - initializing\n");
 
+
 	/* do not load the files if not necessary */
 	if (strlen(default_allow_file) || strlen(default_deny_file)) {
 		if (load_file(default_allow_file, &allow, &allow_rules_num, 1) != 0) goto error;
@@ -313,6 +325,15 @@ static int mod_init(void)
 		perm_destroy_db();
 	}
 
+	if (ip_set_list_malloc(ip_set_list_count, ip_set_list_names) < 0) goto error;
+	if (ip_set_list_count > 0) {
+		ip_set_list_local = pkg_malloc(ip_set_list_count*sizeof(*ip_set_list_local));
+		if (!ip_set_list_local) goto error;
+		memset(ip_set_list_local, 0, sizeof(*ip_set_list_local)*ip_set_list_count);
+	}
+	if (ip_set_list_names) 
+		pkg_free(ip_set_list_names);  /* we need not longer names in pkg memory */
+
 	return 0;
 
 error:
@@ -331,6 +352,8 @@ error:
 	clean_trusted();
 	clean_ipmatch();
 
+	ip_set_list_free();
+
 	return -1;
 }
 
@@ -375,12 +398,22 @@ error:
  */
 static void mod_exit(void)
 {
+	int i;
 	/* free file containers */
 	delete_files(&allow, allow_rules_num);
 	delete_files(&deny, deny_rules_num);
 
 	clean_trusted();
 	clean_ipmatch();
+
+	if (ip_set_list_local) {
+		for (i=0; i<ip_set_list_count; i++) {
+			/* we need delete all cloned sets because might not exist in global list after commit, they have refcnt>1 */
+			if (ip_set_list_local[i])
+				shm_free(ip_set_list_local[i]);
+		}
+	}
+	ip_set_list_free();
 }
 
 
@@ -529,25 +562,97 @@ int w_im_filter(struct sip_msg *msg, char *str1, char *str2)
 	return ipmatch_filter(msg, str1, str2);
 }
 
-
 struct ip_set_param {
-	str s;
-	unsigned int sz;
-	struct ip_set ip_set;
-	fparam_t *fparam;
+	enum {IP_SET_PARAM_KIND_GLOBAL, IP_SET_PARAM_KIND_LOCAL} kind;
+	union {
+		struct {
+			str s;
+			unsigned int sz;
+			struct ip_set ip_set;
+			fparam_t *fparam;
+		} local;
+		struct {
+			struct ip_set_list_item *ip_set;
+		} global;			
+	};
 };
 
 #define MODULE_NAME "permissions"
 
+static inline int is_ip_set_name(str *s) {
+	return (s->len && ((s->s[0] >= 'A' && s->s[0] <= 'Z') || (s->s[0] >= 'a' && s->s[0] <= 'z') || s->s[0] == '_'));
+}
+
+
+static int fixup_param_declare_ip_set( modparam_t type, void* val) {
+	str *p;
+	int i;
+	str s;
+	for (i=0; i<ip_set_list_count; i++) {
+		if (strcmp(val, ip_set_list_names[i].s) == 0) {
+			ERR(MODULE_NAME": declare_ip_set: ip set '%s' already exists\n", (char*)val);
+			return E_CFG;
+		}
+	}
+	s.s = val;
+	s.len = strlen(s.s);
+	if (!is_ip_set_name(&s)) {
+		ERR(MODULE_NAME": declare_ip_set: ip set '%s' is not correct identifier\n", (char*)val);
+		return E_CFG;
+	}
+	p = pkg_realloc(ip_set_list_names, sizeof(*p)*(ip_set_list_count+1));
+	if (!p) return E_OUT_OF_MEM;
+	p[ip_set_list_count] = s;
+	ip_set_list_count++;
+	ip_set_list_names = p;
+	return E_OK;
+};
+	
+
 static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) {
 	str ip_set_s, ip_s;
 	struct ip_addr *ip, ip_buf;
-	struct ip_set new_ip_set;
-	
-	if (get_str_fparam(&ip_set_s, msg, ((struct ip_set_param*)_ip_set)->fparam) < 0) {
-	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip_set parameter value\n");
-	    return -1;
+	struct ip_set new_ip_set, *ip_set;
+	struct ip_set_list_item *isli = NULL;
+	int kind;
+	kind = ((struct ip_set_param*)_ip_set)->kind;
+	if (kind == IP_SET_PARAM_KIND_LOCAL) {
+		if (get_str_fparam(&ip_set_s, msg, ((struct ip_set_param*)_ip_set)->local.fparam) < 0) {
+		    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip_set parameter value\n");
+			return -1;
+		}
+		if (is_ip_set_name(&ip_set_s)) {
+			isli = ip_set_list_find_by_name(ip_set_s);
+			if (!isli) {
+				ERR(MODULE_NAME": ip_is_trusted: ip set '%.*s' is not declared\n", ip_set_s.len, ip_set_s.s);
+				return -1;
+			}
+			kind = IP_SET_PARAM_KIND_GLOBAL;
+			goto force_global;
+		}		
+		ip_set = &((struct ip_set_param*)_ip_set)->local.ip_set;
+	}
+	else {
+		isli = ((struct ip_set_param*)_ip_set)->global.ip_set;
+	force_global:
+		if (!isli->ip_set) return -1; /* empty ip set */
+		
+		if (unlikely(isli->ip_set != ip_set_list_local[isli->idx])) {   /* global ip set has changed ? */
+			if (ip_set_list_local[isli->idx]) {
+				if (atomic_dec_and_test(&ip_set_list_local[isli->idx]->refcnt)) {
+					ip_set_destroy(&ip_set_list_local[isli->idx]->ip_set);
+					shm_free(ip_set_list_local[isli->idx]);
+					ip_set_list_local[isli->idx] = NULL;
+				}
+			}
+			lock_get(&isli->read_lock);			
+			atomic_inc(&isli->ip_set->refcnt);
+			ip_set_list_local[isli->idx] = isli->ip_set;
+			lock_release(&isli->read_lock);
+		}
+		ip_set = &ip_set_list_local[isli->idx]->ip_set;
 	}
+	
 	if (get_str_fparam(&ip_s, msg, (fparam_t*)_ip) < 0) {
 	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip parameter value\n");
 	    return -1;
@@ -583,29 +688,33 @@ static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) {
 	}
 
 	/* test if ip_set string has changed since last call */
-	if (((struct ip_set_param*)_ip_set)->s.len != ip_set_s.len || 
-		memcmp(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.s, ip_set_s.len) != 0) {
+	if (kind == IP_SET_PARAM_KIND_LOCAL) {
+		if (((struct ip_set_param*)_ip_set)->local.s.len != ip_set_s.len || 
+			memcmp(((struct ip_set_param*)_ip_set)->local.s.s, ip_set_s.s, ip_set_s.len) != 0) {
 
-		if (ip_set_parse(&new_ip_set, ip_set_s) < 0) {
-			return -1;
-		};
-		if (((struct ip_set_param*)_ip_set)->sz < ip_set_s.len) {
-			void *p;
-			p = pkg_realloc(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.len);
-			if (!p) {
+			ip_set_init(&new_ip_set, 0);
+			if (ip_set_add_list(&new_ip_set, ip_set_s) < 0) {
 				ip_set_destroy(&new_ip_set);
-				return E_OUT_OF_MEM;
+				return -1;
+			};
+			if (((struct ip_set_param*)_ip_set)->local.sz < ip_set_s.len) {
+				void *p;
+				p = pkg_realloc(((struct ip_set_param*)_ip_set)->local.s.s, ip_set_s.len);
+				if (!p) {
+					ip_set_destroy(&new_ip_set);
+					return E_OUT_OF_MEM;
+				}
+				((struct ip_set_param*)_ip_set)->local.s.s = p;			
+				((struct ip_set_param*)_ip_set)->local.sz = ip_set_s.len;			
 			}
-			((struct ip_set_param*)_ip_set)->s.s = p;			
-			((struct ip_set_param*)_ip_set)->sz = ip_set_s.len;			
+			memcpy(((struct ip_set_param*)_ip_set)->local.s.s, ip_set_s.s, ip_set_s.len);
+			((struct ip_set_param*)_ip_set)->local.s.len = ip_set_s.len;
+			ip_set_destroy(&((struct ip_set_param*)_ip_set)->local.ip_set);
+			((struct ip_set_param*)_ip_set)->local.ip_set = new_ip_set;
 		}
-		memcpy(((struct ip_set_param*)_ip_set)->s.s, ip_set_s.s, ip_set_s.len);
-		((struct ip_set_param*)_ip_set)->s.len = ip_set_s.len;
-		ip_set_destroy(&((struct ip_set_param*)_ip_set)->ip_set);
-		((struct ip_set_param*)_ip_set)->ip_set = new_ip_set;
-/* ip_set_print(stderr, &((struct ip_set_param*)_ip_set)->ip_set); */
 	}
-	switch (ip_set_ip_exists(&((struct ip_set_param*)_ip_set)->ip_set, ip)) {
+/* ip_set_print(stderr, &ip_set); */
+	switch (ip_set_ip_exists(ip_set, ip)) {
 		case IP_TREE_FIND_FOUND:
 		case IP_TREE_FIND_FOUND_UPPER_SET:
 			return 1;
@@ -615,24 +724,41 @@ static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) {
 }
 
 static int fixup_ip_is_trusted(void** param, int param_no) {
-	int ret;
+	int ret = E_CFG;
 	struct ip_set_param *p;
+	str s;
 	if (param_no == 1) {
-
-		ret = fixup_var_str_12(param, param_no);
-		if (ret < 0) return ret;
+		
 		p = pkg_malloc(sizeof(*p));
 		if (!p) return E_OUT_OF_MEM;
 		memset(p, 0, sizeof(*p));
-		ip_set_init(&p->ip_set);
-		p->fparam = *param;
-		*param = p;
+		s.s = *param;
+		s.len = strlen(s.s);
+
+		if (is_ip_set_name(&s)) {
+			p->global.ip_set = ip_set_list_find_by_name(s);
+			if (!p->global.ip_set) {
+				ERR(MODULE_NAME": fixup_ip_is_trusted: ip set '%.*s' is not declared\n", s.len, s.s);			
+				goto err;
+			}
+			p->kind = IP_SET_PARAM_KIND_GLOBAL;
+		} else {
+			ret = fixup_var_str_12(param, param_no);
+			if (ret < 0) goto err;
+			ip_set_init(&p->local.ip_set, 0);
+			p->local.fparam = *param;
+			*param = p;
+			p->kind = IP_SET_PARAM_KIND_LOCAL;
+		}
 	}
 	else {
 		return fixup_var_str_12(param, param_no);
 	
 	}
 	return E_OK;
+err:
+	pkg_free(p);
+	return ret;
 }
 
 

+ 6 - 0
modules_s/permissions/permissions_rpc.h

@@ -32,11 +32,17 @@
 #include "../../rpc.h"
 #include "trusted_rpc.h"
 #include "im_rpc.h"
+#include "ip_set_rpc.h"
 
 rpc_export_t permissions_rpc[] = {
 	{"trusted.reload", trusted_reload, trusted_reload_doc, 0},
 	{"trusted.dump",   trusted_dump,   trusted_dump_doc,   RET_ARRAY},
 	{"ipmatch.reload", im_reload,      im_reload_doc, 0},
+	{"ipset.clean",    rpc_ip_set_clean, rpc_ip_set_clean_doc, 0},
+	{"ipset.add",      rpc_ip_set_add, rpc_ip_set_add_doc, 0},
+	{"ipset.commit",   rpc_ip_set_commit, rpc_ip_set_commit_doc, 0},
+	{"ipset.list",     rpc_ip_set_list, rpc_ip_set_list_doc, RET_ARRAY},
+	{"ipset.print",    rpc_ip_set_print, rpc_ip_set_print_doc, RET_ARRAY},
 	{0, 0, 0, 0}
 };