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