Browse code

pike: Import pike.top rpc call from modules_s/pike

Thanks to Ovidiu for pointers in the right direction.

Olle E. Johansson authored on 20/12/2012 17:51:52
Showing 10 changed files
... ...
@@ -36,7 +36,11 @@ Bogdan-Andrei Iancu
36 36
 
37 37
               5.1. pike_list
38 38
 
39
-   2. Developer Guide
39
+   2. RPC calls
40
+
41
+        1. pike.top
42
+
43
+   3. Developer Guide
40 44
 
41 45
    List of Examples
42 46
 
... ...
@@ -45,7 +49,7 @@ Bogdan-Andrei Iancu
45 49
    1.3. Set remove_latency parameter
46 50
    1.4. Set pike_log_level parameter
47 51
    1.5. pike_check_req usage
48
-   2.1. Tree of IP addresses
52
+   3.1. Tree of IP addresses
49 53
 
50 54
 Chapter 1. Admin Guide
51 55
 
... ...
@@ -170,7 +174,7 @@ modparam("pike", "pike_log_level", -1)
170 174
 
171 175
    4.1. pike_check_req()
172 176
 
173
-4.1. pike_check_req()
177
+4.1.  pike_check_req()
174 178
 
175 179
    Process the source IP of the current request and returns false if the
176 180
    IP was exceeding the blocking limit.
... ...
@@ -196,7 +200,7 @@ if (!pike_check_req()) { exit; };
196 200
 
197 201
    5.1. pike_list
198 202
 
199
-5.1. pike_list
203
+5.1.  pike_list
200 204
 
201 205
    Lists the nodes in the pike tree.
202 206
 
... ...
@@ -208,12 +212,38 @@ if (!pike_check_req()) { exit; };
208 212
                 :pike_list:_reply_fifo_file_
209 213
                 _empty_line_
210 214
 
211
-Chapter 2. Developer Guide
215
+Chapter 2. RPC calls
216
+
217
+   Table of Contents
218
+
219
+   1. pike.top
220
+
221
+1.  pike.top
222
+
223
+   Pike.top behaves like a 'top' command and shows source IP addresses of
224
+   incoming requestes to pike_check_req() function.
225
+
226
+   The IP list is sorted by sum of leaf hits (prev and curr) descending
227
+   and in second level by hits.
228
+
229
+   Some IPs could be marked as HOT depending on theirs request rates.
230
+
231
+   pike.top command takes one string parameter which specifies what kind
232
+   of nodes you are interested in. Possible values are HOT or ALL. If no
233
+   argument is given, it behaves as HOT was used.
234
+
235
+   Marking nodes HOT is done on server side, client only presents given
236
+   data and make some postprocessing like sorting.
237
+
238
+   Output of this command is a simple dump of ip_tree nodes marked as
239
+   ip-leafs.
240
+
241
+Chapter 3. Developer Guide
212 242
 
213 243
    One single tree (for both IPv4 and IPv6) is used. Each node contains a
214 244
    byte, the IP addresses stretching from root to the leafs.
215 245
 
216
-   Example 2.1. Tree of IP addresses
246
+   Example 3.1. Tree of IP addresses
217 247
            / 193 - 175 - 132 - 164
218 248
 tree root /                  \ 142
219 249
           \ 195 - 37 - 78 - 163
... ...
@@ -38,6 +38,6 @@
38 38
 	
39 39
 	<xi:include href="pike_admin.xml"/>
40 40
 	<xi:include href="pike_devel.xml"/>
41
-	
41
+	<xi:include href="pike_rpc.xml"/>
42 42
 	
43 43
 </book>
44 44
new file mode 100644
... ...
@@ -0,0 +1,35 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE section PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" 
3
+   "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
4
+
5
+<section id="pike.rpc" xmlns:xi="http://www.w3.org/2001/XInclude">
6
+	<sectioninfo>
7
+	</sectioninfo>
8
+
9
+	<title>RPC calls</title>
10
+
11
+	<section id="pike.top">
12
+		<title>
13
+			<function>pike.top</function>
14
+		</title>
15
+		<simpara>
16
+			Pike.top behaves like a 'top' command and shows source IP addresses of incoming requestes to pike_check_req() function.
17
+		</simpara>
18
+		<simpara>
19
+			The IP list is sorted by sum of leaf hits (prev and curr) descending and in second level by hits.
20
+		</simpara>
21
+		<simpara>
22
+			Some IPs could be marked as HOT depending on theirs request rates.
23
+		</simpara>
24
+		<simpara>
25
+			pike.top command takes one string parameter which specifies what kind of nodes you are interested in. Possible values
26
+			are HOT or ALL. If no argument is given, it behaves as HOT was used.
27
+		</simpara>
28
+		<simpara>
29
+			Marking nodes HOT is done on server side, client only presents given data and make some postprocessing like sorting.
30
+		</simpara>
31
+		<simpara>
32
+			Output of this command is a simple dump of ip_tree nodes marked as ip-leafs.
33
+		</simpara>
34
+	</section>
35
+</section>
... ...
@@ -148,7 +148,7 @@ error:
148 148
 	return -1;
149 149
 }
150 150
 
151
-
151
+unsigned int get_max_hits() { return root != 0 ? root->max_hits : -1; } 
152 152
 
153 153
 /* destroy an ip_node and all nodes under it; the nodes must be first removed
154 154
  * from any other lists/timers */
... ...
@@ -263,6 +263,20 @@ int is_node_hot_leaf(struct ip_node *node)
263 263
 	return is_hot_leaf(node);
264 264
 }
265 265
 
266
+/*! \brief Used by the rpc function */
267
+char *node_status_array[] = {"", "WARM", "HOT", "ALL"};
268
+node_status_t node_status(struct ip_node *node)
269
+{
270
+        if ( is_hot_leaf(node) )
271
+                return NODE_STATUS_HOT;
272
+
273
+        if ( is_warm_leaf(node) )
274
+                return NODE_STATUS_WARM;
275
+
276
+        return NODE_STATUS_OK;
277
+}
278
+
279
+
266 280
 
267 281
 /* mark with one more hit the given IP address - */
268 282
 struct ip_node* mark_node(unsigned char *ip,int ip_len,
... ...
@@ -369,3 +383,44 @@ void remove_node(struct ip_node *node)
369 383
 	destroy_ip_node(node);
370 384
 }
371 385
 
386
+static void print_node(struct ip_node *node,int sp, FILE *f)
387
+{
388
+	struct ip_node *foo;
389
+
390
+	/* print current node */
391
+	if (!f) {
392
+		DBG("[l%d] node %p; brh=%d byte=%d flags=%d, hits={%d,%d} , "
393
+			"leaf_hits={%d,%d]\n",
394
+			sp, node, node->branch, node->byte, node->flags,
395
+			node->hits[PREV_POS],node->hits[CURR_POS],
396
+			node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]);
397
+	} else {
398
+		fprintf(f,"[l%d] node %p; brh=%d byte=%d flags=%d, hits={%d,%d} , "
399
+			"leaf_hits={%d,%d]\n",
400
+			sp, node, node->branch, node->byte, node->flags,
401
+			node->hits[PREV_POS],node->hits[CURR_POS],
402
+			node->leaf_hits[PREV_POS],node->leaf_hits[CURR_POS]);
403
+	}
404
+
405
+	/* print all the kids */
406
+	foo = node->kids;
407
+	while(foo){
408
+		print_node(foo,sp+1,f);
409
+		foo = foo->next;
410
+	}
411
+}
412
+
413
+void print_tree(  FILE *f )
414
+{
415
+	int i;
416
+
417
+	DBG("DEBUG:pike:print_tree: printing IP tree\n");
418
+	for(i=0;i<MAX_IP_BRANCHES;i++) {
419
+		if (prv_get_tree_branch(i)==0)
420
+			continue;
421
+		prv_lock_tree_branch(i);
422
+		if (prv_get_tree_branch(i))
423
+			print_node( prv_get_tree_branch(i), 0, f);
424
+		prv_unlock_tree_branch(i);
425
+	}
426
+}
... ...
@@ -96,6 +96,17 @@ void lock_tree_branch(unsigned char b);
96 96
 void unlock_tree_branch(unsigned char b);
97 97
 struct ip_node* get_tree_branch(unsigned char b);
98 98
 
99
+typedef enum {
100
+        NODE_STATUS_OK    = 0,
101
+        NODE_STATUS_WARM  = 1,
102
+        NODE_STATUS_HOT   = 2,
103
+        NODE_STATUS_ALL   = 3   /** used for status matching */
104
+} node_status_t;
105
+node_status_t node_status(struct ip_node *node);
106
+extern char *node_status_array[];
107
+unsigned int get_max_hits();
108
+
109
+void print_tree( FILE *f);
99 110
 
100 111
 
101 112
 #endif
... ...
@@ -49,6 +49,8 @@
49 49
 #include "timer.h"
50 50
 #include "pike_mi.h"
51 51
 #include "pike_funcs.h"
52
+#include "../../rpc_lookup.h"
53
+#include "pike_rpc.h"
52 54
 
53 55
 MODULE_VERSION
54 56
 
... ...
@@ -110,11 +112,17 @@ struct module_exports exports= {
110 112
 
111 113
 static int pike_init(void)
112 114
 {
115
+	LOG(L_INFO, "PIKE - initializing\n");
116
+
113 117
 	if(register_mi_mod(exports.name, mi_cmds)!=0)
114 118
 	{
115 119
 		LM_ERR("failed to register MI commands\n");
116 120
 		return -1;
117 121
 	}
122
+	if (rpc_register_array(pike_rpc_methods)!=0) {
123
+		LM_ERR("failed to register RPC commands\n");
124
+		return -1;
125
+	}
118 126
 
119 127
 	/* alloc the timer lock */
120 128
 	timer_lock=lock_alloc();
121 129
new file mode 100644
... ...
@@ -0,0 +1,193 @@
1
+#include "ip_tree.h"
2
+#include "../../rpc_lookup.h"
3
+/*??? #include "rpc.h" */
4
+/*??? #include "top.h" */
5
+#include "../../timer.h"	/* ticks_t */	
6
+
7
+#include "../../dprint.h"
8
+#include "pike_top.h"
9
+
10
+#include <stdlib.h>
11
+#include <unistd.h>
12
+#include <assert.h>
13
+// TODO FIXME LCH remove arpa/inet.h after testing
14
+#include <arpa/inet.h>
15
+
16
+// IPv6 address is a 16 bytes long
17
+#define MAX_DEPTH 16
18
+
19
+static unsigned int g_max_hits = 0;
20
+
21
+static void traverse_subtree( struct ip_node *node, int depth, int options )
22
+{
23
+	static unsigned char ip_addr[MAX_DEPTH];
24
+
25
+	struct ip_node *foo;
26
+	
27
+	DBG("pike:rpc traverse_subtree, depth: %d, byte: %d", depth, node->byte);
28
+
29
+	assert( depth < MAX_DEPTH );
30
+	
31
+	ip_addr[depth] = node->byte;
32
+
33
+	if ( node->flags & NODE_IPLEAF_FLAG ) {
34
+		int ns = node_status(node);
35
+		DBG("pike:traverse_subtree: options: 0x%02x, node status: 0x%02x", options, ns);
36
+		/* add to the result list if it has requested status */
37
+		switch (options) {
38
+			case NODE_STATUS_HOT:
39
+						if ( ns & NODE_STATUS_HOT )
40
+							pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns);
41
+						break;
42
+			case NODE_STATUS_ALL:
43
+						pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns);
44
+						break;
45
+		}
46
+	}
47
+	else if (! node->kids) {	/* TODO non IP leaf of ip_tree - it is possible to report WARM nodes here */
48
+/*		if ( options == ns )
49
+			pike_top_add_entry(ip_addr, depth+1, node->leaf_hits, node->hits, node->expires - get_ticks(), ns);
50
+*/	}
51
+	else {	/* not a any kind of leaf - inner node */
52
+		DBG("pike:rpc traverse_subtree, not IP leaf, depth: %d, ip: %d.%d.%d.%d   hits[%d,%d], expires: %d",
53
+			depth, ip_addr[0], ip_addr[1], ip_addr[2], ip_addr[3], node->hits[0], node->hits[1], node->expires - get_ticks());
54
+	}
55
+	
56
+	foo = node->kids;
57
+	while (foo) {
58
+		traverse_subtree( foo, depth + 1, options );
59
+		foo = foo->next;
60
+	}
61
+}
62
+
63
+static void collect_data(int options)
64
+{
65
+	int i;
66
+
67
+	g_max_hits = get_max_hits();
68
+
69
+	DBG("pike: collect_data");
70
+	
71
+	// maybe try_lock first and than do the rest?
72
+	for(i=0;i<MAX_IP_BRANCHES;i++) {
73
+		if (get_tree_branch(i)==0)
74
+			continue;
75
+		DBG("pike: collect_data: branch %d", i);
76
+		lock_tree_branch(i);
77
+		if (get_tree_branch(i))
78
+			traverse_subtree( get_tree_branch(i), 0, options );
79
+		unlock_tree_branch(i);
80
+    }
81
+}
82
+
83
+/* do not use static buffer with this function */
84
+static const char *concat_err = "ERROR while concatenating string";
85
+static char *concat(char *buff, size_t buffsize, const char *first, int second)
86
+{
87
+	int rv;
88
+	size_t size;
89
+	
90
+	while ( (rv = snprintf(buff, buffsize, "%s%d", first, second)) >= buffsize ) {
91
+		size = rv > 128 ? rv : 128;
92
+		buff = (char *)realloc(buff, size);
93
+		if ( buff == 0 )
94
+			return (char*)concat_err;
95
+		buffsize = size;
96
+		DBG("pike:rpc:concat: new buffer size for %s: %d", first,
97
+				(int)buffsize);
98
+	}
99
+	return buff;
100
+}
101
+
102
+static void pike_top(rpc_t *rpc, void *c)
103
+{
104
+	int i;
105
+	void *handle;
106
+	struct TopListItem_t *top_list_root;
107
+	struct TopListItem_t *ti = 0;
108
+	char addr_buff[40];
109
+	char *ip_addr = 0;
110
+	char *leaf_hits_prev = 0;
111
+	char *leaf_hits_curr = 0;
112
+	char *expires = 0;
113
+	char *status = 0;
114
+	size_t ip_addr_size = 0;
115
+	size_t leaf_hits_prev_size = 0;
116
+	size_t leaf_hits_curr_size = 0;
117
+	size_t expires_size = 0;
118
+	size_t status_size = 0;
119
+	char *stropts;
120
+	int   options = 0;
121
+
122
+	DBG("pike: top");
123
+	
124
+	/* obtain params */
125
+	if (rpc->scan(c, "s", &stropts) <= 0)
126
+		stropts = "HOT";
127
+
128
+	DBG("pike:top: string options: '%s'", stropts);
129
+	if ( strstr(stropts, "ALL") ) { 
130
+		options = NODE_STATUS_ALL;
131
+	} else if ( strstr(stropts, "HOT") ) {
132
+		options |= NODE_STATUS_HOT;
133
+	} else if ( strstr(stropts, "WARM") ) {
134
+		options |= NODE_STATUS_WARM;
135
+	}
136
+	DBG("pike:top: options: 0x%02x\n", options);
137
+	
138
+	
139
+	print_tree( 0 );
140
+	
141
+	collect_data(options);
142
+	top_list_root = pike_top_get_root();
143
+	DBG("pike_top: top_list_root = %p", top_list_root);
144
+	
145
+	rpc->add(c, "{", &handle);
146
+	rpc->struct_add(handle, "d", "max_hits", get_max_hits());
147
+	i = 0; // it is passed as number of rows
148
+	if ( top_list_root == 0 ) {
149
+		DBG("pike_top: no data");
150
+	}
151
+	else {
152
+		for( ti = top_list_root, i = 0; ti != 0; ti = ti->next, ++i ) {
153
+			pike_top_print_addr(ti->ip_addr, ti->addr_len, addr_buff, sizeof(addr_buff));
154
+			DBG("pike:top: result[%d]: %s leaf_hits[%d,%d] hits[%d,%d] expires: %d status: 0x%02x",
155
+					i, addr_buff, ti->leaf_hits[0], ti->leaf_hits[1],
156
+					ti->hits[0], ti->hits[1], ti->expires, ti->status); 
157
+			rpc->struct_add(handle, "sddds",
158
+							concat(ip_addr, ip_addr_size, "ip_addr", i), addr_buff,
159
+							concat(leaf_hits_prev, leaf_hits_prev_size, "leaf_hits_prev", i), ti->leaf_hits[0],
160
+							concat(leaf_hits_curr, leaf_hits_curr_size, "leaf_hits_curr", i), ti->leaf_hits[1],
161
+							concat(expires, expires_size, "expires", i), ti->expires,
162
+							concat(status, status_size, "status", i), node_status_array[ti->status]);
163
+		}
164
+	}
165
+	rpc->struct_add(handle, "d", "number_of_rows", i);
166
+	/* free buffers */
167
+	free(ip_addr);
168
+	free(leaf_hits_prev);
169
+	free(leaf_hits_curr);
170
+	free(expires);
171
+	free(status);
172
+	pike_top_list_clear();
173
+	
174
+	rpc->send(c);
175
+}
176
+
177
+/* ----- exported data structure with methods ----- */
178
+
179
+// TODO check documentation
180
+static const char* pike_top_doc[] = {
181
+	"pike.top doc.",  /* Documentation string */
182
+	0                 /* Method signature(s) */
183
+};
184
+
185
+/* 
186
+ * RPC Methods exported by this module 
187
+ */
188
+
189
+rpc_export_t pike_rpc_methods[] = {
190
+	{"pike.top",   pike_top,     pike_top_doc, 0},
191
+	{0, 0, 0, 0}
192
+};
193
+
0 194
new file mode 100644
... ...
@@ -0,0 +1,9 @@
1
+#ifndef __PIKE_RPC_H
2
+#define __PIKE_RPC_H
3
+
4
+#include "../../rpc.h"
5
+#include "../../rpc_lookup.h"
6
+extern rpc_export_t pike_rpc_methods[];
7
+
8
+
9
+#endif
0 10
new file mode 100644
... ...
@@ -0,0 +1,91 @@
1
+#include "pike_top.h"
2
+
3
+#include "../../dprint.h"
4
+
5
+#include <stdio.h>
6
+#include <stdlib.h>
7
+#include <string.h>
8
+#include <assert.h>
9
+#include <arpa/inet.h>
10
+#include "ip_tree.h"
11
+
12
+
13
+static struct TopListItem_t *top_list_root = 0;
14
+static struct TopListItem_t *top_list_iter = 0;
15
+
16
+static char buff[128];
17
+
18
+struct TopListItem_t *pike_top_get_root() { return top_list_root; }
19
+
20
+char *pike_top_print_addr( unsigned char *ip, int iplen, char *buff, int buffsize )
21
+{
22
+	unsigned short *ipv6_ptr = (unsigned short *)ip;
23
+	memset( buff, 0, sizeof(buff));
24
+	
25
+	DBG("pike:top:print_addr(iplen: %d, buffsize: %d)", iplen, buffsize);
26
+	
27
+	if ( iplen == 4 ) {
28
+		inet_ntop(AF_INET, ip, buff, buffsize);
29
+	}
30
+	else if ( iplen == 16 ) {
31
+		inet_ntop(AF_INET6, ip, buff, buffsize);
32
+	}
33
+	else {
34
+		sprintf( buff, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
35
+				 htons(ipv6_ptr[0]), htons(ipv6_ptr[1]), htons(ipv6_ptr[2]), htons(ipv6_ptr[3]),
36
+				 htons(ipv6_ptr[4]), htons(ipv6_ptr[5]), htons(ipv6_ptr[6]), htons(ipv6_ptr[7]) );
37
+	}
38
+	
39
+	return buff;
40
+}
41
+
42
+/* if you do not need global buffer, you can use this simpler call */
43
+static char *print_addr(unsigned char *ip, int iplen)
44
+{
45
+	return pike_top_print_addr(ip, iplen, buff, sizeof(buff));
46
+}
47
+
48
+int pike_top_add_entry( unsigned char *ip_addr, int addr_len, unsigned int leaf_hits[2], unsigned int hits[2], unsigned int expires, node_status_t status )
49
+{
50
+	struct TopListItem_t *new_item = (struct TopListItem_t *)malloc(sizeof(struct TopListItem_t));
51
+	
52
+	print_addr(ip_addr, addr_len);
53
+	DBG("pike_top_add_enrty(ip: %s, leaf_hits[%d,%d], hits[%d,%d],"
54
+			" expires: %d, status: %d)",
55
+			buff, leaf_hits[0], leaf_hits[1], hits[0], hits[1],
56
+			expires, status);
57
+	assert(new_item != 0);
58
+	
59
+	memset( (void *)new_item, 0, sizeof(struct TopListItem_t) );
60
+	
61
+	new_item->status  = status;
62
+	new_item->expires = expires;
63
+	new_item->hits[0] = hits[0];
64
+	new_item->hits[1] = hits[1];
65
+	new_item->leaf_hits[0] = leaf_hits[0];
66
+	new_item->leaf_hits[1] = leaf_hits[1];
67
+	
68
+	assert( addr_len <= 16 );
69
+	
70
+	new_item->addr_len = addr_len;
71
+	memcpy(new_item->ip_addr, ip_addr, addr_len);
72
+
73
+	new_item->next = top_list_root;
74
+	top_list_root  = new_item;
75
+
76
+	return 1;
77
+}
78
+
79
+void pike_top_list_clear()
80
+{
81
+	struct TopListItem_t *ptr;
82
+	
83
+	top_list_iter = top_list_root;
84
+	while (top_list_iter) {
85
+		ptr = top_list_iter->next;
86
+		free(top_list_iter);
87
+		top_list_iter = ptr;
88
+	}
89
+	top_list_root = 0;
90
+	memset(buff, 0, sizeof(buff));
91
+}
0 92
new file mode 100644
... ...
@@ -0,0 +1,28 @@
1
+#ifndef __PIKE_TOP_H
2
+#define __PIKE_TOP_H
3
+
4
+#include "ip_tree.h"
5
+#include "../../ip_addr.h"
6
+
7
+struct TopListItem_t {
8
+	int             addr_len;
9
+	unsigned char   ip_addr[45];	/*!< Make room for IPv6 */
10
+	unsigned int  	leaf_hits[2];
11
+	unsigned int  	hits[2];
12
+	unsigned int    expires;	/*!< in seconds */
13
+	node_status_t   status;
14
+
15
+	struct TopListItem_t *next;
16
+};
17
+
18
+// returns 1 when OK and 0 when failed
19
+int pike_top_add_entry( unsigned char *ip_addr, int iplen, unsigned int leaf_hits[2], unsigned int hits[2], unsigned int expires, node_status_t status );
20
+
21
+struct TopListItem_t *pike_top_get_root();
22
+void pike_top_list_clear();
23
+
24
+/* helpful functions */
25
+char *pike_top_print_addr( unsigned char *ip_addr, int addrlen, char *buff, int buffsize );
26
+
27
+
28
+#endif // PIKE_TOP_H