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