Browse Source

- 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 years ago
parent
commit
3d2616aeb7

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

@@ -243,7 +243,203 @@ if (method=="REGISTER") {
 	    </programlisting>
 	    </programlisting>
 	</example>
 	</example>
     </section>
     </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>
 	<title>
 	    <function>ip_is_trusted(ip_set, ip)</function>
 	    <function>ip_is_trusted(ip_set, ip)</function>
 	</title>
 	</title>
@@ -257,10 +453,14 @@ if (method=="REGISTER") {
 		<para>
 		<para>
 			<emphasis>ip_set</emphasis> is identified by comma/space/semicolon
 			<emphasis>ip_set</emphasis> is identified by comma/space/semicolon
 			delimited list of IP addresses or subnet masks. The subnet mask
 			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.
 			of particular IP address, i.e. 32 in case of IPv4, 128 in case of IPv6.
 		</para>
 		</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>
 		<listitem>
 		<listitem>
 		<para>
 		<para>
@@ -274,6 +474,9 @@ if (method=="REGISTER") {
 	<example>
 	<example>
 	    <title><function>ip_is_trusted</function> usage</title>
 	    <title><function>ip_is_trusted</function> usage</title>
 	    <programlisting>
 	    <programlisting>
+modparam("permissions", "declare_ipset", "my_ipset1");
+modparam("permissions", "declare_ipset", "my_ipset2");
+	
 route[TT2] {
 route[TT2] {
 	if (ip_is_trusted("$net", "$ip")) {
 	if (ip_is_trusted("$net", "$ip")) {
 		xlog("L_E", "'%$ip' - TRUE\n");
 		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
 	$net = "255.255.255.255/0"; # all IPv4 addresses, dentical to 0.0.0.0/0
 	route(TT1);
 	route(TT1);
 	
 	
-	$net = "127.0.0.1/24";
+	$net = "127.0.0.1/255.255.255.0";
 	route(TT1);
 	route(TT1);
 	
 	
 	$net = "10.0.0.0/8";  # All type A addresses
 	$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";
 	$net = "[0:2:4:A:B:D:E:f300]/120";
 	route(TT1);
 	route(TT1);
 
 
+	$net = "my_ipset1";
+	route(TT1);
 
 
+}
 # the result is:
 # 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]'
 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".
 		Default value is "ipmatch".
 	</para>
 	</para>
     </section>
     </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>
 </section>

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

@@ -163,8 +163,8 @@
 		same as described in Section 1.1.1.
 		same as described in Section 1.1.1.
 	    </para>
 	    </para>
 	</section>
 	</section>
-	<section id="refer_to_permissions">
-	    <title>Refer-To Permissions</title>
+	<section id="Messages catching">
+	    <title>Messages catching</title>
 	    <para>
 	    <para>
 		The module can be also used for catching messages coming from
 		The module can be also used for catching messages coming from
 		or going to specific network elements, for example gateways or
 		or going to specific network elements, for example gateways or
@@ -180,5 +180,6 @@
     
     
     <xi:include href="params.xml"/>
     <xi:include href="params.xml"/>
     <xi:include href="functions.xml"/>
     <xi:include href="functions.xml"/>
+    <xi:include href="xmlrpc.xml"/>
 
 
 </section>
 </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 <stdio.h>
 #include <string.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));
 	memset(ip_set, 0, sizeof(*ip_set));
+	ip_set->use_shm = use_shm;
 	ip_tree_init(&ip_set->ipv4_tree);
 	ip_tree_init(&ip_set->ipv4_tree);
 	#ifdef USE_IPV6
 	#ifdef USE_IPV6
 	ip_tree_init(&ip_set->ipv6_tree);
 	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) {
 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
 	#ifdef USE_IPV6
-	ip_tree_destroy(&ip_set->ipv6_tree, 0);
+	ip_tree_destroy(&ip_set->ipv6_tree, 0, ip_set->use_shm);
 	#endif
 	#endif
 }
 }
 
 
 int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix) {
 int ip_set_add_ip(struct ip_set *ip_set, struct ip_addr *ip, unsigned int network_prefix) {
 	switch (ip->af) {
 	switch (ip->af) {
 		case AF_INET:
 		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
 	#ifdef USE_IPV6
 		case AF_INET6:
 		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
 	#endif
 		default:
 		default:
 			return -1;				
 			return -1;				
@@ -81,62 +82,122 @@ void ip_set_print(FILE *stream, struct ip_set *ip_set) {
 	#endif
 	#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) {
-		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.s++;
 			ip_set_s.len--;
 			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] == '/') {
 		if (ip_set_s.len && ip_set_s.s[0] == '/') {
 			ip_set_s.s++;
 			ip_set_s.s++;
 			ip_set_s.len--;
 			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;
 				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 */
 /* ip_set stuff, combines IPv4 and IPv6 tree in one set */
    
    
 struct ip_set {
 struct ip_set {
+	int use_shm;
 	struct ip_tree_leaf *ipv4_tree;
 	struct ip_tree_leaf *ipv4_tree;
 #ifdef USE_IPV6
 #ifdef USE_IPV6
 	struct ip_tree_leaf *ipv6_tree;	
 	struct ip_tree_leaf *ipv6_tree;	
 #endif
 #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 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(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 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 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
 #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 "ip_tree.h"
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <string.h>
 #include <string.h>
@@ -35,15 +36,18 @@ void ip_tree_init(struct ip_tree_leaf **tree) {
 	*tree = NULL;
 	*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;
 	int i;
 	if (*tree) {
 	if (*tree) {
 		for (i=0; i<=1; i++) {
 		for (i=0; i<=1; i++) {
 			if ((*tree)->next[i])
 			if ((*tree)->next[i])
-				ip_tree_destroy(&(*tree)->next[i], 0);
+				ip_tree_destroy(&(*tree)->next[i], 0, use_shm);
 		}
 		}
 		if (!leaves_only) {
 		if (!leaves_only) {
-			pkg_free(*tree);
+			if (use_shm) 
+			    shm_free(*tree);
+			else
+				pkg_free(*tree);
 			*tree = NULL;
 			*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;
 	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_find h;
 	struct ip_tree_leaf *l0, *l1;
 	struct ip_tree_leaf *l0, *l1;
 	int ret, i, n;
 	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:
 		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 */
 			/* 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;
 			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;
 			break;
 			
 			
 		case IP_TREE_FIND_FOUND:
 		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) {
 			if (h.leaf) {
 				/* split leaf into two leaves */
 				/* split leaf into two leaves */
 				n = h.ip_len - 1;
 				n = h.ip_len - 1;
-				l1 = ip_tree_malloc_leaf(n);
+				l1 = ip_tree_malloc_leaf(n, use_shm);
 				if (!l1) return -1;
 				if (!l1) return -1;
 				l1->prefix_match_len = n;
 				l1->prefix_match_len = n;
 				for (i=0; i<=1; i++)
 				for (i=0; i<=1; i++)
 					l1->next[i] = NULL;				
 					l1->next[i] = NULL;				
 				n = h.leaf->prefix_match_len - h.leaf_prefix_match_len - 1;
 				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) {
 				if (!l0) {
-					ip_tree_destroy(&l1, 0);
+					ip_tree_destroy(&l1, 0, use_shm);
 					return -1;
 					return -1;
 				}
 				}
 				l0->prefix_match_len = n;
 				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 {
 			else {
 				/* it's first leaf in tree */
 				/* 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;
 				if (!*tree) return -1;
 				(*tree)->prefix_match_len = ip_len;
 				(*tree)->prefix_match_len = ip_len;
 				if (ip_len > 0) {
 				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) {
 void ip_tree_print(FILE *stream, struct ip_tree_leaf *tree, unsigned int indent) {
-	unsigned char mask, *pm;
 	unsigned int i, j;
 	unsigned int i, j;
 
 
 	if (!tree) {
 	if (!tree) {
 		fprintf(stream, "nil\n"); 
 		fprintf(stream, "nil\n"); 
 	}
 	}
 	else {
 	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 (j=0; j<=1; j++) {
 			for (i=0; i<indent; i++) fprintf(stream, " ");
 			for (i=0; i<indent; i++) fprintf(stream, " ");
 			fprintf(stream, "%d:", j);
 			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
 #define IP_TREE_FIND_FOUND_UPPER_SET 2
 
 
 extern void ip_tree_init(struct ip_tree_leaf **tree);
 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_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 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
 #endif

+ 161 - 35
modules_s/permissions/permissions.c

@@ -36,10 +36,13 @@
  *               safe_file_load module parameter introduced (Miklos)
  *               safe_file_load module parameter introduced (Miklos)
  *   2006-08-14: child processes do not keep the DB connection open
  *   2006-08-14: child processes do not keep the DB connection open
  *               if cache is enabled (Miklos)
  *               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 <stdio.h>
 #include "../../mem/mem.h"
 #include "../../mem/mem.h"
+#include "../../mem/shm_mem.h"
 #include "permissions.h"
 #include "permissions.h"
 #include "trusted.h"
 #include "trusted.h"
 #include "allow_files.h"
 #include "allow_files.h"
@@ -82,12 +85,17 @@ char	*ipmatch_table = "ipmatch";
 /* Database API */
 /* Database API */
 db_ctx_t	*db_conn = NULL;
 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 */
 /* fixup function prototypes */
 static int fixup_files_1(void** param, int param_no);
 static int fixup_files_1(void** param, int param_no);
 static int fixup_files_2(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_ip_is_trusted(void** param, int param_no);
 static int fixup_w_im(void **, int);
 static int fixup_w_im(void **, int);
 static int fixup_w_im_onsend(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 */
 /* module function prototypes */
 static int allow_routing_0(struct sip_msg* msg, char* str1, char* str2);
 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_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},
 	{"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_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}
         {0, 0, 0, 0, 0}
 };
 };
@@ -145,6 +154,8 @@ static param_export_t params[] = {
 	{"proto_col",          PARAM_STRING, &proto_col         },
 	{"proto_col",          PARAM_STRING, &proto_col         },
 	{"from_col",           PARAM_STRING, &from_col          },
 	{"from_col",           PARAM_STRING, &from_col          },
 	{"ipmatch_table",      PARAM_STRING, &ipmatch_table     },
 	{"ipmatch_table",      PARAM_STRING, &ipmatch_table     },
+	{"declare_ipset",      PARAM_STRING|PARAM_USE_FUNC, fixup_param_declare_ip_set},
+	
         {0, 0, 0}
         {0, 0, 0}
 };
 };
 
 
@@ -272,6 +283,7 @@ static int mod_init(void)
 {
 {
 	LOG(L_INFO, "permissions - initializing\n");
 	LOG(L_INFO, "permissions - initializing\n");
 
 
+
 	/* do not load the files if not necessary */
 	/* do not load the files if not necessary */
 	if (strlen(default_allow_file) || strlen(default_deny_file)) {
 	if (strlen(default_allow_file) || strlen(default_deny_file)) {
 		if (load_file(default_allow_file, &allow, &allow_rules_num, 1) != 0) goto error;
 		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();
 		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;
 	return 0;
 
 
 error:
 error:
@@ -331,6 +352,8 @@ error:
 	clean_trusted();
 	clean_trusted();
 	clean_ipmatch();
 	clean_ipmatch();
 
 
+	ip_set_list_free();
+
 	return -1;
 	return -1;
 }
 }
 
 
@@ -375,12 +398,22 @@ error:
  */
  */
 static void mod_exit(void)
 static void mod_exit(void)
 {
 {
+	int i;
 	/* free file containers */
 	/* free file containers */
 	delete_files(&allow, allow_rules_num);
 	delete_files(&allow, allow_rules_num);
 	delete_files(&deny, deny_rules_num);
 	delete_files(&deny, deny_rules_num);
 
 
 	clean_trusted();
 	clean_trusted();
 	clean_ipmatch();
 	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);
 	return ipmatch_filter(msg, str1, str2);
 }
 }
 
 
-
 struct ip_set_param {
 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"
 #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) {
 static int w_ip_is_trusted(struct sip_msg* msg, char* _ip_set, char* _ip) {
 	str ip_set_s, ip_s;
 	str ip_set_s, ip_s;
 	struct ip_addr *ip, ip_buf;
 	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) {
 	if (get_str_fparam(&ip_s, msg, (fparam_t*)_ip) < 0) {
 	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip parameter value\n");
 	    ERR(MODULE_NAME": ip_is_trusted: Error while obtaining ip parameter value\n");
 	    return -1;
 	    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 */
 	/* 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);
 				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:
 		case IP_TREE_FIND_FOUND_UPPER_SET:
 		case IP_TREE_FIND_FOUND_UPPER_SET:
 			return 1;
 			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) {
 static int fixup_ip_is_trusted(void** param, int param_no) {
-	int ret;
+	int ret = E_CFG;
 	struct ip_set_param *p;
 	struct ip_set_param *p;
+	str s;
 	if (param_no == 1) {
 	if (param_no == 1) {
-
-		ret = fixup_var_str_12(param, param_no);
-		if (ret < 0) return ret;
+		
 		p = pkg_malloc(sizeof(*p));
 		p = pkg_malloc(sizeof(*p));
 		if (!p) return E_OUT_OF_MEM;
 		if (!p) return E_OUT_OF_MEM;
 		memset(p, 0, sizeof(*p));
 		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 {
 	else {
 		return fixup_var_str_12(param, param_no);
 		return fixup_var_str_12(param, param_no);
 	
 	
 	}
 	}
 	return E_OK;
 	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 "../../rpc.h"
 #include "trusted_rpc.h"
 #include "trusted_rpc.h"
 #include "im_rpc.h"
 #include "im_rpc.h"
+#include "ip_set_rpc.h"
 
 
 rpc_export_t permissions_rpc[] = {
 rpc_export_t permissions_rpc[] = {
 	{"trusted.reload", trusted_reload, trusted_reload_doc, 0},
 	{"trusted.reload", trusted_reload, trusted_reload_doc, 0},
 	{"trusted.dump",   trusted_dump,   trusted_dump_doc,   RET_ARRAY},
 	{"trusted.dump",   trusted_dump,   trusted_dump_doc,   RET_ARRAY},
 	{"ipmatch.reload", im_reload,      im_reload_doc, 0},
 	{"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}
 	{0, 0, 0, 0}
 };
 };