improved log parsing
[svn/Prometheus-QoS/.git] / prometheus.c
... / ...
CommitLineData
1 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
2 /* Prometheus QoS - you can "steal fire" from your ISP */\r
3/* "fair-per-IP" quality of service (QoS) utility */\r
4/* requires Linux 2.4.x or 2.6.x with HTB support */\r
5/* Copyright(C) 2005-2008 Michael Polak (xChaos) */\r
6/* iptables-restore support Copyright(C) 2007-2008 ludva */\r
7/* Credit: CZFree.Net,Martin Devera,Netdave,Aquarius,Gandalf */\r
8/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
9\r
10/* Modified: xChaos, 20080422\r
11 ludva, 20080415\r
12\r
13 Prometheus QoS is free software; you can redistribute it and/or\r
14 modify it under the terms of the GNU General Public License as \r
15 published by the Free Software Foundation; either version 2.1 of \r
16 the License, or (at your option) any later version.\r
17\r
18 Prometheus QoS is distributed in the hope that it will be useful,\r
19 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
21 General Public License for more details.\r
22\r
23 You should have received a copy of the GNU General Public License\r
24 along with Prometheus Qos; if not, write to the Free Software\r
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA \r
26 \r
27 GNU General Public License is located in file COPYING */\r
28\r
29#define STRLEN 256\r
30#define FIRSTGROUPID 1024\r
31#define FIRSTIPCLASS 2048\r
32#undef DEBUG\r
33\r
34#include "cll1-0.6.h"\r
35\r
36const char *version="0.7.9-c"; \r
37\r
38/* Version numbers: 0.7.9 will be last development ("beta"), 0.8.0 first stable */\r
39/* Debian(RPM) package versions/patchlevels: 0.7.9-2, 0.8.0-1, 0.8.0-2, etc. */\r
40/* C source code development versions ("beta"): 0.7.9-a, 0.8.1-b, etc. */\r
41/* C source code release versions: 0.8.0, 0.8.2, 0.8.4, etc. */\r
42\r
43/* ======= All path names are defined here (for RPM patch) ======= */\r
44\r
45char *tc = "/sbin/tc"; /* requires tc with HTB support */\r
46char *iptables = "/sbin/iptables"; /* requires iptables utility */\r
47char *iptablessave = "/sbin/iptables-save"; /* not yet required */\r
48char *iptablesrestore = "/sbin/iptables-restore"; /* requires iptables-restore */\r
49char *ls = "/bin/ls"; /* this is not user configurable :-) */\r
50\r
51char *config = "/etc/prometheus/prometheus.conf"; /* main configuration file */\r
52char *hosts = "/etc/prometheus/hosts"; /* per-IP bandwidth definition file */\r
53\r
54char *iptablesfile = "/var/spool/prometheus.iptables"; /* temporary file for iptables-restore*/\r
55char *credit = "/var/lib/misc/prometheus.credit"; /* credit log file */\r
56char *html = "/var/www/traffic.html"; /* hall of fame filename */\r
57char *preview = "/var/www/preview.html"; /* hall of fame preview */\r
58char *cmdlog = "/var/log/prometheuslog"; /* command log filename */\r
59char *log_dir = "/var/www/logs/"; /* log directory pathname, ended with slash */\r
60char *log_url = "logs/"; /* log directory relative URI prefix (partial URL) */\r
61char *html_log_dir = "/var/www/logs/html/";\r
62\r
63/* ======= Help screen is hopefuly self-documenting part of code :-) ======= */\r
64\r
65void help(void)\r
66{\r
67 puts("Command line switches:\n\\r
68\n\\r
69-?, --help this help screen\n\\r
70-v, --version show version number of this utility and exit\n\\r
71-c filename force alternative /etc/prometheus.conf filename\n\\r
72-h filename force alternative /etc/hosts filename (overrides hosts keyword)\n\\r
73-f just flush iptables and tc classes and exit (stop shaping)\n\\r
74-9 emergency iptables flush (do not read data transfer statistics)\n\\r
75-p just generate preview of data transfer statistics and exit\n\\r
76-n no delay (overrides qos-free-delay keyword)\n\\r
77-d dry run (preview tc and iptables commands on stdout)\n\\r
78-l Mmm YYYY generate HTML summary of traffic logs (Mmm=Jan-Dec or Year, YYYY=year)\n\\r
79-m generate HTML summary of traffic logs for yesterday's month\n\\r
80-y generate HTML summary of traffic logs for yesterday's year\n");\r
81/* not yet implemented:\r
82-s start shaping! (keep data transfer statistics - but apply shaping)\n\\r
83-r just reload configuration (...and keep data transfer statistics)\n\\r
84*/\r
85}\r
86\r
87/* === Configuraration file values defaults - stored in global variables ==== */\r
88\r
89int filter_type=1; /*1 mark, 2 classify*/\r
90char *mark="MARK";\r
91char *mark_iptables="MARK --set-mark ";\r
92int dry_run=0; /* preview - use puts() instead of system() */\r
93char *iptablespreamble="*mangle\n:PREROUTING ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\n:INPUT ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]";\r
94FILE *iptables_file=NULL;\r
95int enable_credit=1; /* enable credit file */\r
96int use_credit=0; /* use credit file (if enabled)*/\r
97char *title="Hall of Fame - Greatest Suckers"; /* hall of fame title */\r
98int hall_of_fame=1; /* enable hall of fame */\r
99char *lan="eth0"; /* LAN interface */\r
100char *lan_medium="100Mbit"; /* 10Mbit/100Mbit ethernet */\r
101char *wan="eth1"; /* WAN/ISP interface */\r
102char *wan_medium="100Mbit"; /* 10Mbit/100Mbit ethernet */\r
103char *qos_leaf="sfq perturb 5"; /* leaf discipline */\r
104char *qos_free_zone=NULL; /* QoS free zone */\r
105int qos_proxy=1; /* include proxy port to QoS */\r
106int include_upload=1; /* upload+download=total traffic */\r
107char *proxy_ip="192.168.1.1/32"; /* our IP with proxy port */\r
108int proxy_port=3128; /* proxy port number */\r
109long long int line=1024; /* WAN/ISP download in kbps */\r
110long long int up=1024; /* WAN/ISP upload in kbps */\r
111int free_min=32; /* minimum guaranted bandwidth for all undefined hosts */\r
112int free_max=64; /* maximum allowed bandwidth for all undefined hosts */\r
113int qos_free_delay=0; /* seconds to sleep before applying new QoS rules */\r
114int digital_divide=2; /* controls digital divide weirdness ratio, 1...3 */ \r
115int max_nesting=3; /* maximum nesting of HTB clases, built-in maximum seems to be 4 */\r
116int htb_r2q=1; \r
117int burst=8; /* HTB burst (in kbits) */\r
118int burst_main=64;\r
119int burst_group=32;\r
120int magic_priorities=8; /* number of priority levels (soft shaping) */\r
121int magic_treshold=8; /* reduce ceil by X*magic_treshhold kbps (hard shaping) */\r
122int keywordcount=0;\r
123\r
124/* not yet implemented:\r
125 int fixed_packets=0; maximum number of pps per IP address (not class!) \r
126 int packet_limit=5; maximum number of pps to htn CEIL, not rate !!! \r
127*/\r
128FILE *log_file=NULL;\r
129\r
130char *kwd="via-prometheus"; /* /etc/hosts comment, eg. #qos-64-128 */\r
131\r
132const int idxtable_treshold1=24; /* this is no longer configurable */\r
133const int idxtable_treshold2=12; /* this is no longer configurable */\r
134const int idxtable_bitmask1=3; /* this is no longer configurable */\r
135const int idxtable_bitmask2=3; /* this is no longer configurable */\r
136\r
137/* ==== This is C<<1 stuff - learn C<<1 first! http://cll1.arachne.cz ==== */\r
138\r
139struct IP\r
140{\r
141 char *addr;\r
142 char *name;\r
143 char *sharing;\r
144 int min;\r
145 int desired;\r
146 int max;\r
147 int mark;\r
148 int prio;\r
149 int fixedprio;\r
150 int group;\r
151 unsigned long long direct;\r
152 unsigned long long proxy;\r
153 unsigned long long upload;\r
154 unsigned long long traffic;\r
155 unsigned long long credit;\r
156 unsigned long pktsup;\r
157 unsigned long pktsdown;\r
158 struct Keyword *keyword;\r
159 list(IP);\r
160} *ips=NULL, *ip, *sharedip;\r
161\r
162struct Group\r
163{\r
164 int min;\r
165 int count;\r
166 int desired;\r
167 int id;\r
168 list(Group);\r
169} *groups=NULL, *group;\r
170\r
171struct Index\r
172{\r
173 char *addr;\r
174 char *id;\r
175 struct Index *parent;\r
176 int bitmask;\r
177 int children;\r
178 list(Index);\r
179} *idxs=NULL, *idx, *metaindex;\r
180\r
181struct Keyword\r
182{\r
183 char *key;\r
184 \r
185 int asymetry_ratio; /* ratio for ADSL-like upload */\r
186 int asymetry_fixed; /* fixed treshold for ADSL-like upload */\r
187 int data_limit; /* hard shaping: apply magic_treshold if max*data_limit MB exceeded */\r
188 int data_prio; /* soft shaping (qos): reduce HTB prio if max*data_prio MB exceeded */\r
189 long fixed_limit; /* fixed data limit for setting lower HTB ceil */\r
190 long fixed_prio; /* fixed data lmit for setting lower HTB prio */\r
191 int reserve_min; /* bonus for nominal HTB rate bandwidth (in kbps) */\r
192 int reserve_max; /* malus for nominal HTB ceil (in kbps) */\r
193// int divide_max; /* relative malus: new_ceil=rate+(old_ceil-rate)/divide_max */\r
194// int htb_ceil_bonus_divide; /* relative bonus: new_ceil=old_ceil+old_ceil/htb_ceil_bonus_divide */\r
195 int default_prio; /* default HTB priority for this keyword */\r
196 char *html_color;\r
197 int ip_count;\r
198 char *leaf_discipline;\r
199 \r
200 list(Keyword);\r
201} *keyword,*defaultkeyword=NULL,*keywords=NULL;\r
202\r
203/* Damned, this must be object oriented! This looks almost like constructor ;-) */\r
204\r
205void TheIP(void)\r
206{\r
207 create(ip,IP);\r
208 ip->name="";\r
209 ip->addr="";\r
210 ip->sharing=NULL;\r
211 ip->prio=1;\r
212 ip->fixedprio=0;\r
213 ip->mark=ip->min=ip->max=ip->desired=ip->credit=0;\r
214 ip->upload=ip->proxy=ip->direct=ip->traffic=0;\r
215 ip->pktsup=ip->pktsdown=0;\r
216 ip->keyword=keywords;\r
217 push(ip,ips);\r
218}\r
219\r
220/* ====== iptables indexes are used to reduce complexity to log8(N) ===== */\r
221\r
222char *very_ugly_ipv4_code(char *inip,int bitmask,int format_as_chainname)\r
223{\r
224 /* warning: this function was debugged only for bitmask values 20,24,28 !!!*/\r
225 int dot=0,n;\r
226 char *ip,*outip,*outptr,*fmt;\r
227\r
228 duplicate(inip,ip);\r
229 /* debug printf("(%s,%d) -> ",ip,bitmask); */\r
230\r
231 if(ip && *ip && bitmask>=0 && bitmask<=32)\r
232 string(outip,strlen(ip)+10); /*fuck unicode? assertion: 10>strlen("_%d_%d") */\r
233 else \r
234 /* should never exit here */\r
235 return "undefined";\r
236 outptr=outip;\r
237 while(ip && *ip)\r
238 {\r
239 if(*ip=='.')\r
240 {\r
241 if(dot<(bitmask/8-1)) \r
242 {\r
243 if(format_as_chainname)\r
244 *outptr='_';\r
245 else\r
246 *outptr='.';\r
247 outptr++;\r
248 dot++;\r
249 }\r
250 else\r
251 {\r
252 char *cutdot=strchr(ip+1,'.'); /*for bitmask<24*/\r
253 if(cutdot)*cutdot='\0';\r
254 if(format_as_chainname)\r
255 fmt="_%d_%d";\r
256 else\r
257 fmt=".%d";\r
258 if(bitmask%8)\r
259 n=atoi(ip+1)-atoi(ip+1)%(1<<(8-bitmask%8));\r
260 else\r
261 n=0;\r
262\r
263 /*debug printf("%d/%d => [_%d_%d]\n",atoi(ip+1),bitmask,n,bitmask); */\r
264 sprintf(outptr,fmt,n,bitmask);\r
265 if(!format_as_chainname) while(bitmask<24)\r
266 {\r
267 strcat(outip,".0");\r
268 bitmask+=8;\r
269 }\r
270 /* debug printf("[%s]\n",outip); */\r
271 return outip;\r
272 }\r
273 }\r
274 else \r
275 {\r
276 *outptr=*ip;\r
277 outptr++;\r
278 }\r
279 ip++;\r
280 }\r
281 /*should never exit here*/\r
282 *outptr='\0';\r
283 return outip;\r
284}\r
285\r
286char *hash_id(char *ip,int bitmask)\r
287{ return very_ugly_ipv4_code(ip,bitmask,1); }\r
288\r
289char *subnet_id(char *ip,int bitmask)\r
290{ return very_ugly_ipv4_code(ip,bitmask,0); }\r
291\r
292/* ================= Let's parse configuration file here =================== */\r
293\r
294void reject_config_and_exit(char *filename)\r
295{\r
296 printf("Configuration file %s rejected - abnormal exit.",filename);\r
297 exit(-1);\r
298}\r
299\r
300void get_config(char *config_filename)\r
301{\r
302 char *cnf="mark";\r
303 \r
304 printf("Configured keywords: ");\r
305 parse(config_filename)\r
306 {\r
307 option("keyword",kwd);\r
308 if(kwd)\r
309 {\r
310 printf("%s ",kwd);\r
311\r
312 create(keyword,Keyword);\r
313 keyword->key=kwd;\r
314 keyword->asymetry_ratio=1; /* ratio for ADSL-like upload */\r
315 keyword->asymetry_fixed=0; /* fixed treshold for ADSL-like upload */\r
316 keyword->data_limit=8; /* hard shaping: apply magic_treshold if max*data_limit MB exceeded */\r
317 keyword->data_prio=4; /* soft shaping (qos): reduce HTB prio if max*data_prio MB exceeded */\r
318 keyword->fixed_limit=0; /* fixed data limit for setting lower HTB ceil */\r
319 keyword->fixed_prio=0; /* fixed data limit for setting lower HTB prio */\r
320 keyword->reserve_min=8; /* bonus for nominal HTB rate bandwidth (in kbps) */\r
321 keyword->reserve_max=0; /* malus for nominal HTB ceil (in kbps) */\r
322/* obsolete:\r
323 keyword->divide_max=0; relative malus: new_ceil=rate+(old_ceil-rate)/divide_max\r
324 keyword->htb_ceil_bonus_divide=0; relative bonus: new_ceil=old_ceil+old_ceil/htb_ceil_bonus_divide\r
325*/\r
326 keyword->default_prio=1;\r
327 keyword->html_color="000000";\r
328 keyword->ip_count=0;\r
329 keyword->leaf_discipline="";\r
330\r
331 push(keyword,keywords);\r
332 if(!defaultkeyword) defaultkeyword=keyword;\r
333 keywordcount++;\r
334 \r
335 kwd=NULL;\r
336 }\r
337 else every(keyword,keywords)\r
338 {\r
339 int l=strlen(keyword->key);\r
340\r
341\r
342 if(!strncmp(keyword->key,_,l) && strlen(_)>l+2)\r
343 {\r
344 char *tmptr=_; /* <---- l+1 ----> */\r
345 _+=l+1; /* via-prometheus-asymetry-ratio, etc. */\r
346 ioption("asymetry-ratio",keyword->asymetry_ratio);\r
347 ioption("asymetry-treshold",keyword->asymetry_fixed);\r
348 ioption("magic-relative-limit",keyword->data_limit);\r
349 ioption("magic-relative-prio",keyword->data_prio);\r
350 loption("magic-fixed-limit",keyword->fixed_limit);\r
351 loption("magic-fixed-prio",keyword->fixed_prio);\r
352 ioption("htb-default-prio",keyword->default_prio);\r
353 ioption("htb-rate-bonus",keyword->reserve_min);\r
354 ioption("htb-ceil-malus",keyword->reserve_max);\r
355/* obsolete:\r
356 ioption("htb-ceil-divide",keyword->divide_max);\r
357 ioption("htb-ceil-bonus-divide",keyword->htb_ceil_bonus_divide);\r
358*/\r
359 option("leaf-discipline",keyword->leaf_discipline);\r
360 option("html-color",keyword->html_color);\r
361 _=tmptr;\r
362 \r
363 if(keyword->data_limit || keyword->fixed_limit || \r
364 keyword->data_prio || keyword->fixed_prio)\r
365 use_credit=1; \r
366 }\r
367 }\r
368\r
369 option("tc",tc);\r
370 option("iptables",iptables);\r
371 option("iptables-save",iptablessave); /* new */\r
372 option("iptables-restore",iptablesrestore); /* new */\r
373 option("iptables-file",iptablesfile); /* new */\r
374 option("hosts",hosts);\r
375 option("lan-interface",lan);\r
376 option("wan-interface",wan);\r
377 option("lan-medium",lan_medium);\r
378 option("wan-medium",wan_medium);\r
379 lloption("wan-download",line);\r
380 lloption("wan-upload",up);\r
381 ioption("hall-of-fame-enable",hall_of_fame);\r
382 option("hall-of-fame-title",title);\r
383 option("hall-of-fame-filename",html);\r
384 option("hall-of-fame-preview",preview);\r
385 option("log-filename",cmdlog);\r
386 option("credit-filename",credit);\r
387 ioption("credit-enable",enable_credit);\r
388 option("log-traffic-directory",log_dir);\r
389 option("log-traffic-html-directory",html_log_dir);\r
390 option("log-traffic-url-path",log_url);\r
391 option("qos-free-zone",qos_free_zone);\r
392 ioption("qos-free-delay",qos_free_delay);\r
393 ioption("qos-proxy-enable",qos_proxy);\r
394 option("qos-proxy-ip",proxy_ip);\r
395 option("htb-leaf-discipline",qos_leaf);\r
396 ioption("qos-proxy-port",proxy_port);\r
397 ioption("free-rate",free_min);\r
398 ioption("free-ceil",free_max);\r
399 ioption("htb-burst",burst);\r
400 ioption("htb-burst-main",burst_main);\r
401 ioption("htb-burst-group",burst_group);\r
402 ioption("htb-nesting-limit",max_nesting);\r
403 ioption("htb-r2q",htb_r2q);\r
404 ioption("magic-include-upload",include_upload);\r
405 ioption("magic-priorities",magic_priorities);\r
406 ioption("magic-treshold",magic_treshold); \r
407 option("filter-type", cnf);\r
408 \r
409/* not yet implemented:\r
410 ioption("magic-fixed-packets",fixed_packets);\r
411 ioption("magic-relative-packets",packet_limit);\r
412*/\r
413 }\r
414 fail\r
415 { \r
416 perror(config_filename);\r
417 puts("Warning - using built-in defaults instead ...");\r
418 }\r
419 done;\r
420 printf("\n");\r
421 \r
422 /*leaf discipline for keywords*/\r
423 every(keyword,keywords)\r
424 {\r
425 if (!strcmpi(keyword->leaf_discipline, "")){\r
426 keyword->leaf_discipline = qos_leaf;\r
427 }\r
428 }\r
429\r
430 if (strcmpi(cnf, "mark")){\r
431 filter_type = 2;\r
432 mark = "CLASSIFY";\r
433 mark_iptables = "CLASSIFY --set-class 1:";\r
434 }else{\r
435 filter_type = 1;\r
436 mark = "MARK";\r
437 mark_iptables = "MARK --set-mark ";\r
438 }\r
439\r
440 /* are supplied values meaningful ?*/\r
441 if(line<=0 || up<=0)\r
442 {\r
443 puts("Illegal value of LAN or WAN bandwidth: 0 kbps.");\r
444 reject_config_and_exit(config_filename);\r
445 }\r
446}\r
447\r
448/* ===================== traffic analyser - uses iptables ================ */ \r
449\r
450void get_traffic_statistics(void)\r
451{\r
452 char *str,*cmd;\r
453 int downloadflag=0;\r
454\r
455 textfile(Pipe,str) *line,*lines=NULL;\r
456 string(str,STRLEN);\r
457 string(cmd,STRLEN);\r
458\r
459 sprintf(cmd,"%s -L -v -x -n -t mangle",iptables);\r
460 shell(cmd);\r
461 input(str,STRLEN)\r
462 {\r
463 create(line,Pipe);\r
464 line->str=str;\r
465 string(str,STRLEN);\r
466 append(line,lines);\r
467 }\r
468\r
469 every(line,lines)\r
470 {\r
471 int col, accept=0,proxyflag=0,valid=1,setchainname=0,commonflag=0; \r
472 unsigned long long traffic=0;\r
473 unsigned long pkts=0;\r
474 char *ipaddr=NULL,*ptr;\r
475 \r
476 /* debug puts(line->str); */\r
477 valid_columns(ptr,line->str,' ',col) \r
478 if(valid) switch(col)\r
479 { \r
480 case 1: if(eq(ptr,"Chain"))\r
481 setchainname=1;\r
482 else if(eq(ptr,"pkts")) \r
483 valid=0;\r
484 else\r
485 sscanf(ptr,"%lu",&pkts); \r
486 break;\r
487 case 2: if(setchainname)\r
488 {\r
489 if(!strncmp(ptr,"post_",5) || eq(ptr,"POSTROUTING"))\r
490 downloadflag=1; \r
491 else \r
492 if(!strncmp(ptr,"forw_",5) || eq(ptr,"FORWARD"))\r
493 downloadflag=0;\r
494 \r
495 if(eq(ptr,"post_common") || eq(ptr,"forw_common"))\r
496 commonflag=1;\r
497 }\r
498 else\r
499 sscanf(ptr,"%Lu",&traffic); traffic+=(1<<19); traffic>>=20;\r
500 break;\r
501 case 3: if((strncmp(ptr,"post_",5) && strncmp(ptr,"forw_",5)) || commonflag)\r
502 accept=eq(ptr,mark);\r
503 /*if (filter_type==1) accept=eq(ptr,"MARK"); else accept=eq(ptr,"CLASSIFY");*/\r
504 break;\r
505 case 8: if(downloadflag)\r
506 { \r
507 if(strstr(proxy_ip,ptr))proxyflag=1; \r
508 }\r
509 else\r
510 ipaddr=ptr; \r
511 break;\r
512 case 9: if(downloadflag)ipaddr=ptr;break;\r
513 }\r
514 \r
515 if(accept && traffic>0 && ipaddr)\r
516 {\r
517 if(proxyflag)printf("(proxy) ");\r
518 else if(!downloadflag) printf("(upload) ");\r
519 printf("IP %s: %Lu M (%ld pkts)\n", ipaddr, traffic, pkts);\r
520 find(ip,ips,eq(ip->addr,ipaddr)); \r
521 else \r
522 {\r
523 TheIP();\r
524 ip->addr=ipaddr;\r
525 if(eq(ip->addr,"0.0.0.0/0"))\r
526 {\r
527 ip->name="(unregistered)";\r
528 ip->min=free_min;\r
529 ip->max=ip->desired=free_max;\r
530 }\r
531 }\r
532 \r
533 if(downloadflag)\r
534 {\r
535 if(proxyflag)\r
536 ip->proxy=traffic;\r
537 else\r
538 ip->traffic+=traffic;\r
539 ip->direct=ip->traffic-ip->upload-ip->proxy;\r
540 ip->pktsdown=pkts;\r
541 }\r
542 else\r
543 {\r
544 ip->upload=traffic;\r
545 ip->pktsup=pkts;\r
546 if(include_upload)\r
547 ip->traffic+=traffic;\r
548 else \r
549 if(traffic>ip->traffic)\r
550 ip->traffic=traffic; \r
551 }\r
552 } \r
553 }\r
554\r
555\r
556 free(cmd);\r
557}\r
558 \r
559/* ========== This function executes, logs OR ALSO prints command ========== */\r
560\r
561void safe_run(char *cmd)\r
562{\r
563 if(dry_run) printf("\n=>%s\n",cmd); else system(cmd);\r
564 if(log_file) fprintf(log_file,"%s\n",cmd);\r
565}\r
566\r
567void save_line(char *line)\r
568{\r
569 fprintf(iptables_file,"%s\n",line);\r
570}\r
571\r
572void run_restore(void)\r
573{\r
574 char *restor, *str;\r
575 string(restor,STRLEN);\r
576\r
577 /*-----------------------------------------------------------------*/\r
578 printf("Running %s <%s ...\n",iptablesrestore,iptablesfile);\r
579 /*-----------------------------------------------------------------*/\r
580 \r
581 save_line("COMMIT");\r
582 fclose(iptables_file);\r
583 if(dry_run) \r
584 {\r
585 parse(iptablesfile)\r
586 {\r
587 str=_;\r
588 printf("%s\n", str);\r
589 }done;\r
590 }\r
591\r
592 sprintf(restor,"%s <%s",iptablesrestore, iptablesfile);\r
593 safe_run(restor);\r
594 \r
595 free(restor);\r
596}\r
597\r
598/* == This function strips extra characters after IP address and stores it = */\r
599\r
600void parse_ip(char *str)\r
601{\r
602 char *ptr=str,*ipaddr=NULL,*ipname=NULL;;\r
603 \r
604 while(*ptr && *ptr!=' ' && *ptr!=9)\r
605 ptr++;\r
606 \r
607 *ptr=0;\r
608 ipaddr=str;\r
609 ptr++;\r
610 while(*ptr && (*ptr==' ' || *ptr==9))\r
611 ptr++;\r
612 ipname=ptr; \r
613 while(*ptr && *ptr!=' ' && *ptr!=9)\r
614 ptr++;\r
615 *ptr=0;\r
616\r
617 find(ip,ips,eq(ip->addr,ipaddr)); else TheIP();\r
618 ip->addr=ipaddr;\r
619 ip->name=ipname;\r
620}\r
621\r
622char *parse_datafile_line(char *str)\r
623{\r
624 char *ptr=strchr(str,' ');\r
625\r
626 if(ptr)\r
627 {\r
628 *ptr=0;\r
629 ptr++;\r
630 return ptr;\r
631 } \r
632 else \r
633 return NULL;\r
634}\r
635\r
636struct IpLog\r
637{\r
638 char *name;\r
639 long traffic;\r
640 list(IpLog);\r
641} *iplog,*iplogs;\r
642\r
643void parse_ip_log(int argc, char **argv) \r
644{\r
645 char *month,*year,*str,*name,*ptr,*ptr2;\r
646 long traffic,traffic_month,total=0;\r
647 int col,col2,y_ok,m_ok,accept_month,i=1,any_month=0;\r
648 char mstr[4],ystr[5];\r
649 FILE *f;\r
650 \r
651 string(str,STRLEN);\r
652\r
653 if(argv[1][1]=='l') /* -l */\r
654 {\r
655 if(argc<4)\r
656 {\r
657 puts("Missing parameter(s)!\nUsage: prometheus -l Mmm YYYY (Mmm=Jan-Dec or Year, YYYY=year)");\r
658 exit(-1);\r
659 }\r
660 else\r
661 {\r
662 month=argv[2];\r
663 if(eq(month,"Year")) any_month=1;\r
664 year=argv[3];\r
665 }\r
666 }\r
667 else\r
668 { \r
669 time_t t = time(NULL) - 3600*24 ; /* yesterday's timestamp*/\r
670 struct tm *timep = localtime(&t); \r
671 \r
672 if(argv[1][1]=='m') /* -m yestarday - month */\r
673 {\r
674 strftime(mstr, 4, "%b", timep);\r
675 month=mstr;\r
676 strftime(ystr, 5, "%Y", timep);\r
677 year=ystr; \r
678 }\r
679 else /* -y yesterday - year */\r
680 {\r
681 month="Year";\r
682 any_month=1;\r
683 strftime(ystr, 5, "%Y", timep);\r
684 year=ystr;\r
685 }\r
686 }\r
687 printf("Analysing traffic for %s %s ...\n",month,year);\r
688\r
689 sprintf(str,"%s %s/*.log",ls,log_dir);\r
690 shell(str);\r
691\r
692 input(str,STRLEN)\r
693 {\r
694 ptr=strrchr(str,'\n');\r
695 if(ptr) *ptr='\0';\r
696 printf("Parsing %s ...",str);\r
697 accept_month=0;\r
698 traffic_month=0;\r
699 parse(str)\r
700 {\r
701 y_ok=m_ok=0; \r
702 valid_columns(ptr,_,'\t',col) switch(col)\r
703 {\r
704 case 2: name=ptr;break;\r
705 case 3: traffic=atol(ptr);break;\r
706 /* column number - was 7, now 9...*/\r
707 case 7:\r
708 case 8:\r
709 case 9: if (isalnum(*ptr)) /* alphanumeric string = date, just one*/\r
710 {\r
711 valid_columns(ptr2,ptr,' ',col2) switch(col2)\r
712 {\r
713 case 2: if(any_month || eq(ptr2,month)) m_ok=1; break;\r
714 case 5: if(eq(ptr2,year)) y_ok=1; break;\r
715 }\r
716 }\r
717 }\r
718 \r
719 if(y_ok && m_ok) \r
720 {\r
721 traffic_month+=traffic;\r
722 accept_month=1;\r
723 }\r
724 }\r
725 done;\r
726\r
727 if(accept_month)\r
728 {\r
729 create(iplog,IpLog);\r
730 iplog->name=name;\r
731 iplog->traffic=traffic_month;\r
732 insert(iplog,iplogs,desc_order_by,traffic);\r
733 printf(" %ld MB\n",iplog->traffic);\r
734 }\r
735 else\r
736 puts(" no records.");\r
737 }\r
738 sprintf(str,"%s/%s-%s.html",html_log_dir,year,month);\r
739 printf("Writing %s ...",str);\r
740 f=fopen(str,"w");\r
741 if(f)\r
742 {\r
743 fprintf(f,"<table border><tr><th colspan=\"4\">Data transfers - %s %s</th></tr>\n ",month,year);\r
744 every(iplog,iplogs)\r
745 if(iplog->traffic)\r
746 {\r
747 fprintf(f,"<tr><td align=\"right\">%d</td><th>%s</td><td align=\"right\">%ld MB</td><th align=\"right\">%ld GB</th></tr>\n",i++,iplog->name,iplog->traffic,iplog->traffic>>10);\r
748 total+=iplog->traffic>>10;\r
749 }\r
750 fprintf(f,"<tr><th colspan=\"3\" align=\"left\">Total:</th><th align=\"right\">%ld GB</th></tr>\n",total);\r
751 fputs("</table>\n",f);\r
752 fclose(f);\r
753 puts(" done.");\r
754 }\r
755}\r
756\r
757\r
758/*-----------------------------------------------------------------*/\r
759/* Are you looking for int main (int argc, char **argv) ? :-)) */\r
760/*-----------------------------------------------------------------*/\r
761\r
762program\r
763{\r
764 int i=0;\r
765 FILE *f=NULL;\r
766 char *str, *ptr, *d;\r
767 char *substring;\r
768 int class_count=0,ip_count=0;\r
769 int parent=1;\r
770 int just_flush=0;\r
771 int nodelay=0;\r
772 int just_preview=0; /* preview - generate just stats */\r
773 int just_logs=0; /* just parse logs */\r
774 \r
775 char *chain_forward, *chain_postrouting;\r
776 char *althosts=NULL;\r
777 \r
778 printf("\n\\r
779Prometheus QoS - \"fair-per-IP\" Quality of Service setup utility.\n\\r
780Version %s - Copyright (C)2005-2008 Michael Polak (xChaos)\n\\r
781iptables-restore & burst tunning & classify modification 0.7d by Ludva\n\\r
782Credit: CZFree.Net, Martin Devera, Netdave, Aquarius, Gandalf\n\n",version);\r
783\r
784 /*----- Boring... we have to check command line options first: ----*/\r
785 \r
786 arguments\r
787 {\r
788 argument("-c") { nextargument(config); }\r
789 argument("-h") { nextargument(althosts);}\r
790 argument("-d") { dry_run=1; }\r
791 argument("-f") { just_flush=1; }\r
792 argument("-9") { just_flush=9; }\r
793 argument("-p") { just_preview=1; }\r
794 argument("-n") { nodelay=1; }\r
795 argument("-l") { just_logs=1; }\r
796 argument("-m") { just_logs=1; }\r
797 argument("-y") { just_logs=1; }\r
798 argument("-?") { help(); exit(0); }\r
799 argument("--help") { help(); exit(0); }\r
800 argument("-v") { exit(0); } \r
801 argument("--version") { exit(0); } \r
802 }\r
803\r
804 if(dry_run)\r
805 puts("*** THIS IS JUST DRY RUN ! ***\n");\r
806\r
807 date(d); /* this is typical cll1.h macro */\r
808\r
809 /*-----------------------------------------------------------------*/\r
810 printf("Parsing configuration file %s ...\n", config);\r
811 /*-----------------------------------------------------------------*/\r
812 get_config(config);\r
813 \r
814 if(just_logs)\r
815 {\r
816 parse_ip_log(argc,argv);\r
817 exit(0);\r
818 }\r
819\r
820 if(althosts) hosts=althosts;\r
821\r
822 if(just_flush<9)\r
823 {\r
824 /*-----------------------------------------------------------------*/\r
825 puts("Parsing iptables verbose output ...");\r
826 /*-----------------------------------------------------------------*/\r
827 get_traffic_statistics();\r
828 }\r
829\r
830 /*-----------------------------------------------------------------*/\r
831 printf("Parsing class defintion file %s ...\n", hosts);\r
832 /*-----------------------------------------------------------------*/\r
833 int groupidx = FIRSTGROUPID;\r
834 parse(hosts)\r
835 {\r
836 str=_;\r
837\r
838 if(*str<'0' || *str>'9')\r
839 continue;\r
840 \r
841 //Does this IP share QoS class with some other ?\r
842 substring=strstr(str,"sharing-");\r
843 if(substring)\r
844 { \r
845 substring+=8; //"sharing-"\r
846 parse_ip(str);\r
847 ip_count++;\r
848 ip->sharing=substring;\r
849 ip->keyword=defaultkeyword; /* settings for default keyword */\r
850 while(*substring && *substring!='\n')\r
851 substring++;\r
852 *substring=0; \r
853 }\r
854 else\r
855 {\r
856 //Do we have to create new QoS class for this IP ?\r
857\r
858 find(keyword,keywords,(substring=strstr(str,keyword->key)))\r
859 {\r
860 parse_ip(str);\r
861 ip_count++;\r
862 ip->keyword=keyword;\r
863 keyword->ip_count++;\r
864 ip->prio=keyword->default_prio;\r
865 substring+=strlen(keyword->key)+1;\r
866 ptr=substring;\r
867 while(*ptr && *ptr!='-')\r
868 ptr++;\r
869 if(*ptr=='-')\r
870 {\r
871 *ptr=0;\r
872 ip->max=ip->desired=atoi(ptr+1);\r
873 }\r
874 ip->min=atoi(substring);\r
875 if(ip->min<=0)\r
876 {\r
877 printf(" %s: Illegal value of minimum bandwidth 0 kbps, using %d kbps\n",str,free_min);\r
878 ip->min=free_min;\r
879 }\r
880 if(ip->max<=ip->min)\r
881 {\r
882 ip->fixedprio=1;\r
883 ip->max=ip->min+ip->keyword->reserve_min;\r
884 }\r
885 else \r
886 {\r
887 ip->max-=ip->keyword->reserve_max;\r
888\r
889/*\r
890 if(ip->keyword->divide_max>1)\r
891 ip->max=ip->min+(ip->max-ip->min)/ip->keyword->divide_max;\r
892 if(ip->keyword->htb_ceil_bonus_divide>0)\r
893 ip->max+=ip->max/ip->keyword->htb_ceil_bonus_divide;\r
894*/\r
895 if(ip->max<ip->min)\r
896 ip->max=ip->min;\r
897 }\r
898 ip->mark=FIRSTIPCLASS+1+class_count++;\r
899\r
900 find(group,groups,group->min==ip->min) \r
901 { \r
902 group->count++; \r
903 group->desired+=ip->min;\r
904 ip->group = group->id; \r
905 }\r
906 else\r
907 {\r
908 create(group,Group);\r
909 group->min=ip->min;\r
910 group->id = groupidx++;\r
911 ip->group = group->id;\r
912\r
913 if(group->min<8) group->min=8;\r
914 /* Warning - this is maybe because of primitive tc namespace, can be fixed */\r
915 /* it is because class IDs are derived from min. bandwidth. - xCh */\r
916 //if(group->min>MAX_GUARANTED_KBPS) group->min=MAX_GUARANTED_KBPS;\r
917 \r
918 group->count=1;\r
919 group->desired=ip->min; \r
920 insert(group,groups,desc_order_by,min);\r
921 }\r
922 }//endif keyword-\r
923 }//endif sharing-\r
924 }\r
925 fail\r
926 {\r
927 perror(hosts);\r
928 exit(-1);\r
929 }\r
930 done;\r
931\r
932 /*-----------------------------------------------------------------*/\r
933 /* cll1.h - let's allocate brand new character buffer... */\r
934 /*-----------------------------------------------------------------*/\r
935 string(str,STRLEN); \r
936\r
937 /*-----------------------------------------------------------------*/\r
938 puts("Resolving shared connections ...");\r
939 /*-----------------------------------------------------------------*/\r
940 search(ip,ips,ip->sharing)\r
941 {\r
942 search(sharedip,ips,eq(sharedip->name,ip->sharing))\r
943 {\r
944 sharedip->traffic+=ip->traffic;\r
945 ip->traffic=0;\r
946 ip->mark=sharedip->mark; \r
947 break;\r
948 }\r
949 if(!sharedip)\r
950 printf("Unresolved shared connection: %s %s sharing-%s\n",ip->addr,ip->name,ip->sharing);\r
951 }\r
952\r
953 if(enable_credit && just_flush<9)\r
954 {\r
955 /*-----------------------------------------------------------------*/\r
956 printf("Parsing credit file %s ...\n", credit);\r
957 /*-----------------------------------------------------------------*/\r
958 parse(credit)\r
959 {\r
960 ptr=parse_datafile_line(_);\r
961 if(ptr)\r
962 {\r
963 find(ip,ips,eq(ip->addr,_))\r
964 sscanf(ptr,"%Lu",&(ip->credit));\r
965 }\r
966 }\r
967 done;\r
968 }\r
969\r
970 if(!just_preview)\r
971 {\r
972 /*-----------------------------------------------------------------*/\r
973 puts("Initializing iptables and tc classes ...");\r
974 /*-----------------------------------------------------------------*/\r
975 \r
976 iptables_file=fopen(iptablesfile,"w");\r
977 if (iptables_file == NULL) {\r
978 puts("Cannot open iptablesfile!");\r
979 exit(-1);\r
980 }\r
981 \r
982 log_file=fopen(cmdlog,"w");\r
983 if (log_file == NULL) {\r
984 puts("Cannot open logfile!");\r
985 exit(-1);\r
986 }\r
987 \r
988 save_line(iptablespreamble);\r
989 run_restore();\r
990 \r
991 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,lan);\r
992 safe_run(str);\r
993\r
994 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,wan);\r
995 safe_run(str);\r
996 \r
997 iptables_file=fopen(iptablesfile,"w");\r
998 save_line(iptablespreamble);\r
999\r
1000 if(qos_free_zone && *qos_free_zone!='0')\r
1001 {\r
1002 char *chain;\r
1003 \r
1004 sprintf(str,"-A FORWARD -d %s -o %s -j ACCEPT", qos_free_zone, wan);\r
1005 save_line(str);\r
1006 \r
1007 if(qos_proxy)\r
1008 {\r
1009 save_line(":post_noproxy - [0:0]");\r
1010 sprintf(str,"-A POSTROUTING -p ! tcp -o %s -j post_noproxy", lan);\r
1011 save_line(str); \r
1012 sprintf(str,"-A POSTROUTING -s ! %s -o %s -j post_noproxy", proxy_ip, lan);\r
1013 save_line(str); \r
1014 sprintf(str,"-A POSTROUTING -s %s -p tcp --sport ! %d -o %s -j post_noproxy", proxy_ip, proxy_port, lan);\r
1015 save_line(str); \r
1016\r
1017 chain="post_noproxy"; \r
1018 }\r
1019 else\r
1020 chain="POSTROUTING";\r
1021 \r
1022 sprintf(str,"-A %s -s %s -o %s -j ACCEPT", chain, qos_free_zone, lan);\r
1023 save_line(str);\r
1024 }\r
1025 \r
1026 if(ip_count>idxtable_treshold1 && !just_flush)\r
1027 {\r
1028 int idxcount=0, bitmask=32-idxtable_bitmask1; /* default net mask: 255.255.255.240 */\r
1029 char *subnet, *buf;\r
1030 /*-----------------------------------------------------------------*/\r
1031 printf("Detected %d addresses - indexing iptables rules to improve performance...\n",ip_count);\r
1032 /*-----------------------------------------------------------------*/\r
1033\r
1034 save_line(":post_common - [0:0]");\r
1035 save_line(":forw_common - [0:0]");\r
1036\r
1037 search(ip,ips,ip->addr && *(ip->addr) && !eq(ip->addr,"0.0.0.0/0"))\r
1038 {\r
1039 buf=hash_id(ip->addr,bitmask);\r
1040 find(idx,idxs,eq(idx->id,buf))\r
1041 idx->children++;\r
1042 else\r
1043 {\r
1044 create(idx,Index);\r
1045 idx->addr=ip->addr;\r
1046 idx->id=buf;\r
1047 idx->bitmask=bitmask;\r
1048 idx->parent=NULL;\r
1049 idx->children=0;\r
1050 idxcount++;\r
1051 push(idx,idxs);\r
1052 }\r
1053 }\r
1054\r
1055 /* brutal perfomance optimalization */\r
1056 while(idxcount>idxtable_treshold2 && bitmask>2*idxtable_bitmask2)\r
1057 {\r
1058 bitmask-=idxtable_bitmask2;\r
1059 idxcount=0;\r
1060 search(idx,idxs,idx->parent==NULL)\r
1061 {\r
1062 buf=hash_id(idx->addr,bitmask);\r
1063 find(metaindex,idxs,eq(metaindex->id,buf))\r
1064 metaindex->children++; \r
1065 else\r
1066 {\r
1067 create(metaindex,Index);\r
1068 metaindex->addr=idx->addr;\r
1069 metaindex->id=buf;\r
1070 metaindex->bitmask=bitmask;\r
1071 metaindex->parent=NULL;\r
1072 metaindex->children=0;\r
1073 idxcount++;\r
1074 push(metaindex,idxs);\r
1075 }\r
1076 idx->parent=metaindex;\r
1077 }\r
1078 }\r
1079\r
1080 /* this should slightly optimize throughout ... */\r
1081 sort(idx,idxs,desc_order_by,children);\r
1082 sort(idx,idxs,order_by,bitmask);\r
1083\r
1084 i=0;\r
1085 every(idx,idxs)\r
1086 {\r
1087 subnet=subnet_id(idx->addr,idx->bitmask);\r
1088 printf("%d: %s/%d\n",++i,subnet,idx->bitmask);\r
1089 \r
1090 sprintf(str,":post_%s - [0:0]", idx->id);\r
1091 save_line(str);\r
1092\r
1093 sprintf(str,":forw_%s - [0:0]", idx->id);\r
1094 save_line(str);\r
1095\r
1096 if(idx->parent)\r
1097 {\r
1098 string(buf,strlen(idx->parent->id)+6);\r
1099 sprintf(buf,"post_%s",idx->parent->id);\r
1100 }\r
1101 else\r
1102 buf="POSTROUTING";\r
1103\r
1104 sprintf(str,"-A %s -d %s/%d -o %s -j post_%s", buf, subnet, idx->bitmask, lan, idx->id);\r
1105 save_line(str);\r
1106\r
1107 sprintf(str,"-A %s -d %s/%d -o %s -j post_common", buf, subnet, idx->bitmask, lan);\r
1108 save_line(str);\r
1109\r
1110 if(idx->parent)\r
1111 {\r
1112 string(buf,strlen(idx->parent->id)+6);\r
1113 sprintf(buf,"forw_%s",idx->parent->id);\r
1114 }\r
1115 else\r
1116 buf="FORWARD";\r
1117\r
1118 sprintf(str,"-A %s -s %s/%d -o %s -j forw_%s", buf, subnet, idx->bitmask, wan, idx->id);\r
1119 save_line(str);\r
1120\r
1121 sprintf(str,"-A %s -s %s/%d -o %s -j forw_common", buf, subnet, idx->bitmask, wan);\r
1122 save_line(str);\r
1123 }\r
1124 printf("Total indexed iptables chains created: %d\n", i);\r
1125\r
1126 sprintf(str,"-A FORWARD -o %s -j forw_common", wan);\r
1127 save_line(str);\r
1128 \r
1129 sprintf(str,"-A POSTROUTING -o %s -j post_common", lan);\r
1130 save_line(str);\r
1131 }\r
1132 \r
1133 }\r
1134\r
1135 if(just_flush)\r
1136 {\r
1137 fclose(iptables_file);\r
1138 if (log_file) fclose(log_file);\r
1139 puts("Just flushed iptables and tc classes - now exiting ...");\r
1140 exit(0);\r
1141 }\r
1142\r
1143 if(!just_preview)\r
1144 {\r
1145 if(!dry_run && !nodelay && qos_free_delay)\r
1146 {\r
1147 printf("Flushed iptables and tc classes - now sleeping for %d seconds...\n",qos_free_delay);\r
1148 sleep(qos_free_delay);\r
1149 }\r
1150\r
1151 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,lan,htb_r2q);\r
1152 safe_run(str);\r
1153\r
1154 sprintf(str,"%s class add dev %s parent 1: classid 1:2 htb rate %s ceil %s burst %dk prio 0",tc,lan,lan_medium,lan_medium,burst_main);\r
1155 safe_run(str);\r
1156\r
1157 sprintf(str,"%s class add dev %s parent 1:2 classid 1:1 htb rate %Ldkbit ceil %Ldkbit burst %dk prio 0",tc,lan,line,line,burst_main);\r
1158 safe_run(str);\r
1159\r
1160 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,wan,htb_r2q);\r
1161 safe_run(str);\r
1162\r
1163 sprintf(str,"%s class add dev %s parent 1: classid 1:2 htb rate %s ceil %s burst %dk prio 0",tc,wan,wan_medium,wan_medium,burst_main);\r
1164 safe_run(str);\r
1165\r
1166 sprintf(str,"%s class add dev %s parent 1:2 classid 1:1 htb rate %Ldkbit ceil %Ldkbit burst %dk prio 0",tc,wan,up,up,burst_main);\r
1167 safe_run(str);\r
1168 }\r
1169\r
1170 /*-----------------------------------------------------------------*/\r
1171 puts("Locating suckers and generating root classes ...");\r
1172 /*-----------------------------------------------------------------*/\r
1173 sort(ip,ips,desc_order_by,traffic);\r
1174 \r
1175\r
1176 /*-----------------------------------------------------------------*/\r
1177 /* sub-scope - local variables */ \r
1178 {\r
1179 long long int rate=line;\r
1180 long long int max=line;\r
1181 int group_count=0;\r
1182 FILE *credit_file=NULL;\r
1183 \r
1184 if(!just_preview && !dry_run && enable_credit) credit_file=fopen(credit,"w");\r
1185 \r
1186 every(group,groups)\r
1187 {\r
1188 if(!just_preview)\r
1189 {\r
1190 \r
1191 //download\r
1192 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %Ldkbit ceil %Ldkbit burst %dk prio 1 #down desired %d", \r
1193 tc, lan, parent, group->id, rate, max, burst_group, group->desired);\r
1194 safe_run(str);\r
1195 \r
1196 //upload\r
1197 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %Ldkbit ceil %Ldkbit burst %dk prio 1 #up desired %d", \r
1198 tc, wan, parent, group->id, rate*up/line, max*up/line, burst_group, group->desired);\r
1199 safe_run(str);\r
1200 }\r
1201 \r
1202 if(group_count++<max_nesting) parent=group->id;\r
1203 \r
1204 rate-=digital_divide*group->min;\r
1205 if(rate<group->min)rate=group->min;\r
1206 \r
1207 /*shaping of aggresive downloaders, with credit file support */\r
1208 if(use_credit)\r
1209 {\r
1210 int group_rate=group->min, priority_sequence=magic_priorities+1;\r
1211 \r
1212 search(ip, ips, ip->min==group->min && ip->max>ip->min)\r
1213 {\r
1214 if( ip->keyword->data_limit && !ip->fixedprio &&\r
1215 ip->traffic>ip->credit+\r
1216 (ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20)) )\r
1217 {\r
1218 if(group_rate<ip->max) ip->max=group_rate;\r
1219 group_rate+=magic_treshold;\r
1220 ip->prio=magic_priorities+2;\r
1221 if(ip->prio<3) ip->prio=3;\r
1222 }\r
1223 else\r
1224 {\r
1225 if( ip->keyword->data_prio && !ip->fixedprio &&\r
1226 ip->traffic>ip->credit+\r
1227 (ip->min*ip->keyword->data_prio+(ip->keyword->fixed_prio<<20)) )\r
1228 {\r
1229 ip->prio=priority_sequence--;\r
1230 if(ip->prio<2) ip->prio=2;\r
1231 }\r
1232 \r
1233 if(credit_file)\r
1234 {\r
1235 unsigned long long lcredit=0;\r
1236 \r
1237 if((ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))>ip->traffic) \r
1238 lcredit=(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))-ip->traffic;\r
1239 fprintf(credit_file,"%s %Lu\n",ip->addr,lcredit);\r
1240 }\r
1241 }\r
1242 }\r
1243 \r
1244 }\r
1245 }\r
1246 if(credit_file)fclose(credit_file);\r
1247 }\r
1248\r
1249 if(just_preview)\r
1250 {\r
1251 f=fopen(preview,"w");\r
1252 ptr=preview; \r
1253 }\r
1254 else if(!dry_run && !just_flush)\r
1255 {\r
1256 /*-----------------------------------------------------------------*/\r
1257 printf("Writing data transfer database ...\n");\r
1258 /*-----------------------------------------------------------------*/\r
1259 f=fopen("/var/run/prometheus.previous","w");\r
1260 if(f)\r
1261 {\r
1262 search(ip,ips,ip->traffic || ip->direct || ip->proxy ||ip->upload)\r
1263 fprintf(f,"%s %Lu %Lu %Lu %Lu\n",ip->addr,ip->traffic,ip->direct,ip->proxy,ip->upload);\r
1264 fclose(f);\r
1265 }\r
1266\r
1267 f=fopen(html,"w");\r
1268 ptr=html;\r
1269 }\r
1270\r
1271 if(f)\r
1272 {\r
1273 int total=0;\r
1274 int count=1;\r
1275 i=0;\r
1276\r
1277 /*-----------------------------------------------------------------*/\r
1278 printf("Sorting data and generating statistics page %s ...\n",ptr);\r
1279 /*-----------------------------------------------------------------*/\r
1280\r
1281 fputs("<table border>\n<tr><th align=\"right\">#</th><th align=\"right\">group</th><th align=\"right\">IPs</th><th align=\"right\">requested</th>\n",f);\r
1282 fprintf(f,"<th colspan=\"%d\">data limits</th>\n",keywordcount);\r
1283 fputs("</tr>\n",f);\r
1284 every(group,groups) \r
1285 { \r
1286#ifdef DEBUG\r
1287 printf("%d k group: %d bandwidth requested: %d k\n",group->min,group->count,group->desired);\r
1288#endif\r
1289 fprintf(f,"<tr><td align=\"right\">%d</td><td align=\"right\">%d k</td>",count,group->min);\r
1290 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d k</td>",group->count,group->desired);\r
1291\r
1292 every(keyword,keywords)\r
1293 fprintf(f,"<td align=\"right\"><font color=\"#%s\">%d M</font></td>",keyword->html_color,group->min*keyword->data_limit); \r
1294 \r
1295 i+=group->desired; \r
1296 total+=group->count;\r
1297 count++; \r
1298 }\r
1299#ifdef DEBUG\r
1300 printf("Total groups: %d Total bandwidth requested: %d k\nAGGREGATION: 1/%d\n",count,i,i/line);\r
1301#endif\r
1302 fprintf(f,"<tr><th colspan=\"2\" align=\"left\">Line %Ld k</td>",line);\r
1303 fprintf(f,"<th align=\"right\">%d</td><th align=\"right\">%d k</td>",total,i);\r
1304\r
1305 every(keyword,keywords)\r
1306 fprintf(f,"<th align=\"right\">%d IPs</th>",keyword->ip_count); \r
1307\r
1308 fprintf(f,"</tr><tr><th colspan=\"4\">Aggregation 1/%d</th>\n",(int)(0.5+i/line));\r
1309 fprintf(f,"<th colspan=\"%d\">%d traffic classes</th></tr>\n",keywordcount,total);\r
1310\r
1311 fputs("</table>\n",f);\r
1312 }\r
1313 else if(!dry_run && !just_flush) \r
1314 perror(html);\r
1315\r
1316 i=1;\r
1317 if(f)\r
1318 {\r
1319 unsigned long long total=0, total_direct=0, total_proxy=0, total_upload=0, tmp_sum=0;\r
1320 int active_classes=0;\r
1321 int colspan;\r
1322 FILE *iplog;\r
1323 struct Sum {unsigned long long l; int i; list(Sum);} *sum,*sums=NULL;\r
1324\r
1325 if(qos_proxy)\r
1326 colspan=12;\r
1327 else \r
1328 colspan=11;\r
1329 \r
1330 fprintf(f,"<p><table border>\n<tr><th colspan=\"%d\">%s",colspan,title);\r
1331 fprintf(f," (%s)</th></tr>\n", d);\r
1332 fputs("<tr><td align=\"right\">#</td><td>hostname</td>\\r
1333 <td align=\"right\">credit</td>\\r
1334 <td align=\"right\">limit</td>\\r
1335 <td align=\"right\">total</td>\\r
1336 <td align=\"right\">direct</td>\n",f);\r
1337 if(qos_proxy)\r
1338 fputs("<td align=\"right\">proxy</td>\n",f);\r
1339 fputs("<td align=\"right\">upload</td>\\r
1340 <td align=\"right\">minimum</td>\\r
1341 <td align=\"right\">desired</td>\\r
1342 <td align=\"right\">maximum</td>\\r
1343 <td>prio</td></tr>\n",f); \r
1344\r
1345 every(ip,ips)\r
1346 {\r
1347 char *f1="", *f2="";\r
1348 if(ip->max<ip->desired)\r
1349 {\r
1350 f1="<font color=\"red\">";\r
1351 f2="</font>";\r
1352 }\r
1353 else if(ip->prio>1)\r
1354 {\r
1355 f1="<font color=\"brown\">";\r
1356 f2="</font>";\r
1357 }\r
1358\r
1359#ifdef DEBUG\r
1360 printf("%03d. %-22s %10Lu (%d/%d)\n",i ,ip->name, ip->traffic, ip->min, ip->max); \r
1361#endif\r
1362 fprintf(f,"<tr><td align=\"right\"><a name=\"%s\"></a>%d</td><td><a href=\"%s%s.log\">%s</a></td><td align=\"right\">%Lu M</td>\n",\r
1363 ip->name, i, log_url, ip->name, ip->name, ip->credit);\r
1364 fprintf(f,"<td align=\"right\"><font color=\"#%s\">%Lu M</font></td>",ip->keyword->html_color,ip->credit+(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20)));\r
1365 fprintf(f,"<td align=\"right\">%s%Lu M%s</td><td align=\"right\">%Lu M</td>\n", f1, ip->traffic, f2, ip->direct);\r
1366 if(qos_proxy)\r
1367 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->proxy);\r
1368 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->upload);\r
1369 fprintf(f,"<td align=\"right\">%d k</td><td align=\"right\">%d k</td><td align=\"right\">%s%d k%s</td><td>%s%d%s</td></tr>\n",ip->min,ip->desired,f1,ip->max,f2,f1,ip->prio,f2);\r
1370 total+=ip->traffic;\r
1371 total_direct+=ip->direct;\r
1372 total_proxy+=ip->proxy;\r
1373 total_upload+=ip->upload;\r
1374 if(ip->traffic>0)\r
1375 {\r
1376 active_classes++;\r
1377 tmp_sum+=ip->traffic;\r
1378 create(sum,Sum);\r
1379 sum->l=tmp_sum;\r
1380 sum->i=active_classes;\r
1381 insert(sum,sums,order_by,i);\r
1382 }\r
1383 \r
1384 i++;\r
1385 \r
1386 if(!just_preview)\r
1387 {\r
1388 sprintf(str,"%s/%s.log",log_dir,ip->name);\r
1389 iplog=fopen(str,"a");\r
1390 if(iplog)\r
1391 {\r
1392 fprintf(iplog,"%ld\t%s\t%Lu\t%Lu\t%Lu\t%Lu\t%d\t%d\t%d\t%s",\r
1393 time(NULL),ip->name,ip->traffic,ip->direct,ip->proxy,ip->upload,ip->min,ip->max,ip->desired,d); /* d = date*/\r
1394 fclose(iplog);\r
1395 }\r
1396 }\r
1397\r
1398 }\r
1399 fprintf(f,"<tr><th colspan=\"4 \"align=\"left\">SUMMARY:</td>");\r
1400 fprintf(f,"<th align=\"right\">%Lu M</th>\\r
1401 <th align=\"right\">%Lu M</th>\n", total, total_direct);\r
1402 if(qos_proxy)\r
1403 fprintf(f,"<th align=\"right\">%Lu M</th>\n", total_proxy);\r
1404 fprintf(f,"<th align=\"right\">%Lu M</th>", total_upload);\r
1405 fputs("<td colspan=\"4\"></td></th>\n</table>\n",f);\r
1406\r
1407 if(active_classes>10)\r
1408 {\r
1409 fputs("<a name=\"erp\"></a><p><table border><tr><th colspan=\"5\">Enterprise Research and Planning (ERP)</th></tr>\n",f);\r
1410 fputs("<tr><td>Analytic category</td>\n",f);\r
1411 fputs("<td colspan=\"2\" align=\"center\">Active Classes</td><td colspan=\"2\" align=\"center\">Data transfers</td></tr>\n",f);\r
1412\r
1413 find(sum,sums,sum->l>=total/4)\r
1414 {\r
1415 fprintf(f,"<tr><td>Top 25%% of traffic</td>\n");\r
1416 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1417 }\r
1418 \r
1419 find(sum,sums,sum->i==10)\r
1420 {\r
1421 fprintf(f,"<tr><td>Top 10 downloaders</td>\n");\r
1422 fprintf(f,"<th align=\"right\">10</th><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1423 }\r
1424\r
1425 find(sum,sums,sum->l>=total/2)\r
1426 {\r
1427 fprintf(f,"<tr><td>Top 50%% of traffic</td>\n");\r
1428 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><th align=\"right\">%Ld %%</th></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1429 }\r
1430\r
1431 find(sum,sums,sum->l>=4*total/5)\r
1432 {\r
1433 fprintf(f,"<tr><td>Top 80%% of traffic</td>\n");\r
1434 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><th align=\"right\">%Ld %%</th></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1435 }\r
1436\r
1437 find (sum,sums,sum->i>=(active_classes+1)/5)\r
1438 {\r
1439 fprintf(f,"<tr><td>Top 20%% downloaders</td>\n");\r
1440 fprintf(f,"<td align=\"right\">%d</td><th align=\"right\">%d %%</th><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1441 }\r
1442\r
1443 find(sum,sums,sum->i>=(active_classes+1)/4)\r
1444 {\r
1445 fprintf(f,"<tr><td>Top 25%% downloaders</td>\n");\r
1446 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1447 }\r
1448\r
1449 find(sum,sums,sum->i>=(active_classes+1)/2)\r
1450 {\r
1451 fprintf(f,"<tr><td>Top 50%% downloaders</td>\n");\r
1452 fprintf(f,"<td align=\"right\">%d</td><th align=\"right\">%d %%</th><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1453 }\r
1454\r
1455 find(sum,sums,sum->i>=4*(active_classes+1)/5)\r
1456 {\r
1457 fprintf(f,"<tr><td>Top 80%% downloaders</td>\n");\r
1458 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);\r
1459 }\r
1460\r
1461 fprintf(f,"<tr><td>All users, all traffic</td>\n");\r
1462 fprintf(f,"<th align=\"right\">%d</th><th align=\"right\">100 %%</th><th align=\"right\">%Lu M</th><th align=\"right\">100 %%</th></tr>\n",active_classes,total);\r
1463 fputs("</table>\n",f);\r
1464 }\r
1465 fprintf(f,"<small>Statistics generated by Prometheus QoS version %s<br>GPL+Copyright(C)2005-2008 Michael Polak, <a href=\"http://www.arachne.cz/\">Arachne Labs</a></small>\n",version);\r
1466 fclose(f);\r
1467 }\r
1468\r
1469 if(just_preview)\r
1470 {\r
1471 puts("Statistics preview generated (-p switch) - now exiting ...");\r
1472 exit(0);\r
1473 }\r
1474 \r
1475 /*-----------------------------------------------------------------*/\r
1476 puts("Generating iptables and tc classes ...");\r
1477 /*-----------------------------------------------------------------*/\r
1478\r
1479 i=0;\r
1480 printf("%-22s %-15s mark\n","name","ip");\r
1481 search(ip,ips,ip->mark>0)\r
1482 { \r
1483 \r
1484 if(idxs)\r
1485 {\r
1486 char *buf;\r
1487 duplicate(ip->addr,buf);\r
1488 buf=hash_id(ip->addr,32-idxtable_bitmask1); \r
1489 \r
1490 string(chain_forward,6+strlen(buf));\r
1491 strcpy(chain_forward,"forw_");\r
1492 strcat(chain_forward,buf);\r
1493\r
1494 string(chain_postrouting,6+strlen(buf));\r
1495 strcpy(chain_postrouting,"post_");\r
1496 strcat(chain_postrouting,buf);\r
1497 \r
1498 free(buf);\r
1499 }\r
1500 else\r
1501 {\r
1502 chain_forward="FORWARD";\r
1503 chain_postrouting="POSTROUTING";\r
1504 }\r
1505\r
1506 printf("%-22s %-16s %04d ", ip->name, ip->addr, ip->mark); \r
1507\r
1508 /* -------------------------------------------------------- mark download */\r
1509 \r
1510 sprintf(str,"-A %s -d %s/32 -o %s -j %s%d",chain_postrouting,ip->addr,lan,mark_iptables,ip->mark);\r
1511 /*sprintf(str,"-A %s -d %s/32 -o %s -j MARK --set-mark %d",chain_postrouting,ip->addr,lan,ip->mark);*/\r
1512 /* -m limit --limit 1/s */ \r
1513 save_line(str);\r
1514\r
1515 if(qos_proxy)\r
1516 {\r
1517 sprintf(str,"-A %s -s %s -p tcp --sport %d -d %s/32 -o %s -j %s%d",chain_postrouting,proxy_ip,proxy_port,ip->addr,lan,mark_iptables,ip->mark);\r
1518 /*sprintf(str,"-A %s -s %s -p tcp --sport %d -d %s/32 -o %s -j MARK --set-mark %d",chain_postrouting,proxy_ip,proxy_port,ip->addr,lan,ip->mark);*/\r
1519 save_line(str);\r
1520 }\r
1521\r
1522 sprintf(str,"-A %s -d %s/32 -o %s -j ACCEPT",chain_postrouting,ip->addr,lan);\r
1523 save_line(str);\r
1524\r
1525 /* -------------------------------------------------------- mark upload */\r
1526 sprintf(str,"-A %s -s %s/32 -o %s -j %s%d",chain_forward,ip->addr,wan,mark_iptables,ip->mark);\r
1527 /* sprintf(str,"-A %s -s %s/32 -o %s -j MARK --set-mark %d",chain_forward,ip->addr,wan,ip->mark);*/\r
1528 save_line(str);\r
1529\r
1530 sprintf(str,"-A %s -s %s/32 -o %s -j ACCEPT",chain_forward,ip->addr,wan);\r
1531 save_line(str);\r
1532\r
1533 if(ip->min)\r
1534 {\r
1535 /* -------------------------------------------------------- download class */\r
1536 printf("(down: %dk-%dk ", ip->min, ip->max); \r
1537\r
1538 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d", tc, lan, ip->group, ip->mark,ip->min,ip->max, burst, ip->prio);\r
1539 safe_run(str);\r
1540\r
1541 if (strcmpi(ip->keyword->leaf_discipline, "none")){\r
1542 sprintf(str,"%s qdisc add dev %s parent 1:%d handle %d %s", tc, lan, ip->mark, ip->mark, ip->keyword->leaf_discipline); /*qos_leaf*/\r
1543 safe_run(str);\r
1544 }\r
1545 \r
1546 if (filter_type == 1){\r
1547 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d", tc, lan, ip->mark, ip->mark);\r
1548 safe_run(str);\r
1549 }\r
1550\r
1551 /* -------------------------------------------------------- upload class */\r
1552 printf("up: %dk-%dk)\n", (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), \r
1553 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed));\r
1554\r
1555 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",\r
1556 tc, wan, ip->group, ip->mark,\r
1557 (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),\r
1558 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), burst, ip->prio);\r
1559 safe_run(str);\r
1560 \r
1561 if (strcmpi(ip->keyword->leaf_discipline, "none")){\r
1562 sprintf(str,"%s qdisc add dev %s parent 1:%d handle %d %s",tc, wan, ip->mark, ip->mark, ip->keyword->leaf_discipline); /*qos_leaf*/\r
1563 safe_run(str);\r
1564 }\r
1565 \r
1566 if (filter_type == 1){\r
1567 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d",tc, wan, ip->mark, ip->mark);\r
1568 safe_run(str);\r
1569 }\r
1570 }\r
1571 else\r
1572 printf("(sharing %s)\n", ip->sharing);\r
1573 i++;\r
1574 }\r
1575\r
1576\r
1577 if(idxs)\r
1578 {\r
1579 chain_forward="forw_common";\r
1580 chain_postrouting="post_common";\r
1581 }\r
1582 else\r
1583 {\r
1584 chain_forward="FORWARD";\r
1585 chain_postrouting="POSTROUTING";\r
1586 }\r
1587\r
1588 /* -------------------------------------------------------- mark download */\r
1589\r
1590 if(qos_proxy)\r
1591 {\r
1592 sprintf(str,"-A %s -s %s -p tcp --sport %d -o %s -j %s%d",chain_postrouting,proxy_ip,proxy_port,lan,mark_iptables,3);\r
1593 save_line(str);\r
1594 sprintf(str,"-A %s -s %s -p tcp --sport %d -o %s -j ACCEPT",chain_postrouting,proxy_ip,proxy_port,lan);\r
1595 save_line(str);\r
1596 }\r
1597 sprintf(str,"-A %s -o %s -j %s%d",chain_postrouting,lan,mark_iptables,3);\r
1598 save_line(str);\r
1599 sprintf(str,"-A %s -o %s -j ACCEPT",chain_postrouting,lan);\r
1600 save_line(str);\r
1601\r
1602 /* -------------------------------------------------------- mark upload */\r
1603 sprintf(str,"-A %s -o %s -j %s%d",chain_forward,wan,mark_iptables,3);\r
1604 save_line(str);\r
1605 sprintf(str,"-A %s -o %s -j ACCEPT",chain_forward,wan);\r
1606 save_line(str);\r
1607\r
1608 printf("Total IP count: %d\n", i);\r
1609\r
1610 /*-----------------------------------------------------------------*/\r
1611 puts("Generating free bandwith classes ...");\r
1612 /*-----------------------------------------------------------------*/\r
1613\r
1614 /* ---------------------------------------- tc - free bandwith shared class */\r
1615 sprintf(str,"%s class add dev %s parent 1:%d classid 1:3 htb rate %dkbit ceil %dkbit burst %dk prio 2",tc,lan,parent,free_min,free_max,burst);\r
1616 safe_run(str);\r
1617\r
1618 sprintf(str,"%s class add dev %s parent 1:%d classid 1:3 htb rate %dkbit ceil %dkbit burst %dk prio 2",tc,wan,parent,free_min,free_max,burst);\r
1619 safe_run(str);\r
1620\r
1621 /* tc SFQ */\r
1622 if (strcmpi(qos_leaf, "none")){\r
1623 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,lan,qos_leaf);\r
1624 safe_run(str);\r
1625 \r
1626 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,wan,qos_leaf);\r
1627 safe_run(str);\r
1628 }\r
1629 \r
1630 /* tc handle 1 fw flowid */\r
1631 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,lan);\r
1632 safe_run(str);\r
1633\r
1634 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,wan);\r
1635 safe_run(str);\r
1636\r
1637 run_restore();\r
1638 \r
1639 if (log_file) fclose(log_file);\r
1640 return 0;\r
1641\r
1642 /* that's all folks, thank you for reading it all the way up to this point ;-) */\r
1643 /* bad luck C<<1 is not yet finished, I promise no sprintf() next time... */\r
1644}\r
This page took 0.301697 seconds and 4 git commands to generate.