From: "Stanisalv V. Bogatyrev" <s.bogatyrev@kenjitsu.net>
To: netfilter-devel@lists.netfilter.org
Subject: ACC and port numers
Date: Wed, 26 Nov 2003 12:17:24 +0300 [thread overview]
Message-ID: <3FC46FA4.6060503@kenjitsu.net> (raw)
[-- Attachment #1: Type: text/plain, Size: 2236 bytes --]
Hello, All!
I need to add port and protocol accounting to ACC module.
http://lists.netfilter.org/pipermail/netfilter-devel/2001-March/000719.html
Original patch:
http://zzz.corg.ru/u/Members/dmitry/iptables-1.2-acc.patch/view
I added some code, but I get broken port nubers on incoming traffic.
I'm new in netfilter/iptables programming and can't find a mistake. Help
me please, what do I need to correct to make it work?
Sources are attached to this letter.
ipacc output:
# 1069835594 1069835643 0 1
## Wed Nov 26 11:33:14 2003
## Wed Nov 26 11:34:03 2003
192.168.10.202 1920 216.180.243.82 20480 6 3 1 1177
216.180.243.82 69 192.168.10.202 15360 6 1 2 180
216.180.243.82 69 192.168.10.202 13312 6 1 2 624
216.180.243.82 69 192.168.10.202 23557 6 1 2 36000
216.180.243.82 69 192.168.10.202 2563 6 1 2 906
192.168.10.202 2176 216.180.243.82 20480 6 3 1 3718
216.180.243.82 69 192.168.10.202 18693 6 1 2 1481
216.180.243.82 69 192.168.10.202 18176 6 1 2 199
192.168.10.202 2432 216.180.243.82 20480 6 3 1 2218
216.180.243.82 69 192.168.10.202 24836 6 1 2 1249
216.180.243.82 69 192.168.10.202 15876 6 1 2 1086
216.180.243.82 69 192.168.10.202 24580 6 1 2 1248
216.180.243.82 69 192.168.10.202 31490 6 1 2 635
216.180.243.82 69 192.168.10.202 6402 6 1 2 537
216.180.243.82 69 192.168.10.202 5889 6 1 2 279
216.180.243.82 69 192.168.10.202 1537 6 1 2 262
216.180.243.82 69 192.168.10.202 13316 6 1 2 1076
216.180.243.82 69 192.168.10.202 25604 6 1 2 2504
216.180.243.82 69 192.168.10.202 22273 6 1 2 471
192.168.10.202 2688 207.171.179.30 20480 6 3 1 814
207.171.179.30 69 192.168.10.202 15360 6 1 2 60
207.171.179.30 69 192.168.10.202 13312 6 1 2 52
207.171.179.30 69 192.168.10.202 23557 6 1 2 1500
207.171.179.30 69 192.168.10.202 1027 6 1 2 900
192.168.10.202 2944 207.171.183.19 20480 6 3 1 886
192.168.10.202 3200 207.171.183.19 20480 6 3 1 784
207.171.183.19 69 192.168.10.202 15360 6 1 2 120
207.171.183.19 69 192.168.10.202 13312 6 1 2 104
207.171.183.19 69 192.168.10.202 23557 6 1 2 4500
207.171.183.19 69 192.168.10.202 30468 6 1 2 1271
207.171.183.19 69 192.168.10.202 15105 6 1 2 315
207.171.183.19 69 192.168.10.202 24324 6 1 2 1119
--
Stanislav Bogatyrev
Kenjitsu
mailto: s.bogatyrev@kenjitsu.net
[-- Attachment #2: ipacc.c --]
[-- Type: text/plain, Size: 2846 bytes --]
#include <stdio.h>
#include <errno.h>
#include <time.h>
#include <getopt.h>
#include <arpa/inet.h>
#include <asm/types.h>
#include <linux/netfilter_ipv4/ipt_ACC.h>
static int fd = -1;
static int op;
static int hash_size = 1;
static int mem_limit = 16;
static void usage(char *msg)
{
if (msg) fprintf(stderr, "ERROR: %s\n", msg);
fprintf(stderr,"Usage: ipacc [-LFsm]\n");
exit(1);
}
static void error(int err,char *msg)
{
fprintf(stderr," %s %s\n",msg,strerror(err));
exit(1);
}
inline void dotted(__u32 addr)
{
unsigned char *c = (unsigned char *) &addr;
printf("%d.%d.%d.%d ",c[0],c[1],c[2],c[3]);
}
static void printblock(struct ip_acc_get_block *blk)
{
int i;
struct acc_entry *e;
for(i=0;i<ACC_ENTRIES_PER_BLOCK;i++) {
e= &blk->bl[i];
if(e->count == 0)
break;
dotted(e->src);
printf("%d ",e->sprt);
dotted(e->dst);
printf("%d ",e->dprt);
printf("%d ",e->proto);
printf("%d ",e->hooknum);
printf("%d %llu\n",e->mark,e->count);
}
}
static void do_list()
{
int i;
struct ip_acc_get_info info;
struct ip_acc_get_block req;
int info_len=sizeof(info);
int req_len=sizeof(req);
if (0 > getsockopt(fd, SOL_IP, SO_IP_ACC_INFO, &info, &info_len))
error(errno," failed: ");
printf("# %lu\t%lu\t%llu\t%d\n",info.time_on.tv_sec,
info.time_off.tv_sec,info.lost,info.blocks);
printf("## %s",ctime(&info.time_on.tv_sec));
printf("## %s",ctime(&info.time_off.tv_sec));
for(i=0;i < info.blocks; i++ ) {
req.block=i;
if (0 > getsockopt(fd, SOL_IP, SO_IP_ACC_BLOCK, &req, &req_len))
error(errno," failed:");
printblock(&req);
}
printf("\n");
}
static void do_flush()
{
struct ip_acc_set_rq req;
int reqlen = sizeof(req);
req.op = op & ( IP_ACC_SET_FLUSH | IP_ACC_SET_HASH | IP_ACC_SET_LIMIT );
req.hash_size = hash_size;
req.mem_limit = mem_limit;
if (0 > setsockopt(fd, SOL_IP, SO_IP_ACC, &req, sizeof(req)) )
error(errno," failed:");
}
int main(int argc, char **argv)
{
int opt;
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd < 0)
error(errno,"cannot get DGRAM socket:");
op = IP_ACC_SET_NONE;
while (EOF != (opt=getopt( argc, argv, "hLFs:m:")))
switch(opt) {
case 'F':
op |= IP_ACC_SET_FLUSH;
break;
case 'L':
op |= IP_ACC_GET_LIST;
break;
case 's':
op |= IP_ACC_SET_HASH;
hash_size=atoi(optarg);
switch(hash_size) {
case 1:
case 4:
case 16:
break;
default:
usage("hash size should be 1,4 or 16 blocks");
}
break;
case 'm':
op |= IP_ACC_SET_LIMIT;
mem_limit=atoi(optarg);
if( mem_limit < 16 || mem_limit > 1024 )
usage("memory limit only from 16 to 1024 allowed");
break;
case 'h':
usage(0);
default:
usage("bad option");
}
if (op == IP_ACC_SET_NONE)
usage("no operation specified");
if (op & IP_ACC_SET_FLUSH)
do_flush();
if (op & IP_ACC_GET_LIST)
do_list();
return 0;
}
[-- Attachment #3: ipt_ACC.c --]
[-- Type: text/plain, Size: 8500 bytes --]
#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <asm/uaccess.h>
#include <linux/mm.h>
#include <linux/time.h>
/* Include all protocols we supposed to know headers of */
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/icmp.h>
#ifndef CONFIG_NETFILTER
#define CONFIG_NETFILTER
#endif
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_ACC.h>
#include <linux/netfilter_ipv4/lockhelp.h>
static DECLARE_RWLOCK(acc_lock);
static struct acc_ctrl main_table;
static struct acc_ctrl back_table;
static int hash_size = 4096;
static int hash_module = 1021;
static int mem_limit = 16;
static void free_table(struct acc_ctrl *table)
{
struct acc_block *blk;
while( (blk=table->heap) ) {
table->heap=blk->next;
kfree(blk);
}
if(table->hash)
kfree(table->hash);
memset(table,0,sizeof(struct acc_ctrl));
}
static void init_table(struct acc_ctrl *tbl)
{
memset(tbl,0,sizeof(struct acc_ctrl));
tbl->hash_size=hash_size;
tbl->hash_module=hash_module;
if( (tbl->hash=kmalloc(tbl->hash_size,GFP_KERNEL)) == 0 ) {
printk("ACC: hash allocation failure\n");
} else
memset(tbl->hash,0,tbl->hash_size);
tbl->last_heap = tbl->heap = kmalloc(ACC_HEAP_BLK_SZ,GFP_KERNEL);
if( tbl->heap != 0) {
memset(tbl->heap,0,ACC_HEAP_BLK_SZ);
tbl->blocks_count=1;
tbl->free_count=ACC_ENTRIES_PER_BLOCK;
}
}
inline void backup_table(void)
{
struct acc_ctrl tmp;
struct timeval t;
free_table(&back_table);
init_table(&tmp);
do_gettimeofday(&t);
WRITE_LOCK(&acc_lock);
memcpy(&back_table,&main_table,sizeof(struct acc_ctrl));
memcpy(&main_table,&tmp,sizeof(struct acc_ctrl));
memcpy(&back_table.time_off,&t,sizeof(struct timeval));
memcpy(&main_table.time_on,&t,sizeof(struct timeval));
WRITE_UNLOCK(&acc_lock);
}
inline int add_heap_blk(void)
{
struct acc_block *blk;
if( main_table.blocks_count >= mem_limit || (blk=kmalloc(ACC_HEAP_BLK_SZ,GFP_ATOMIC)) == 0)
return 0;
memset(blk,0,ACC_HEAP_BLK_SZ);
if( ! main_table.heap )
main_table.heap = blk;
else if( main_table.last_heap )
main_table.last_heap->next = blk;
main_table.last_heap = blk;
// skip blk->next = NULL because blk is already zeroed
main_table.free_count = ACC_ENTRIES_PER_BLOCK;
main_table.blocks_count++;
return 1;
}
//FIXME: Write a better hash function for use with protoz and ports
inline int hash_func_acc(__u32 src,__u32 dst,__u32 mark)
{
return (
( (src % 4073) << 20) + ( (dst % 4079) << 8 ) +
(mark % 251) ) % main_table.hash_module;
}
inline struct acc_entry *new_entry(__u32 src, __u16 sprt, __u32 dst, __u16 dprt, __u32 mark,__u32 count, __u8 proto, __u8 hooknum)
{
struct acc_entry *e;
if( main_table.free_count == 0 && add_heap_blk() == 0)
return NULL;
e = &main_table.last_heap->bl[ACC_ENTRIES_PER_BLOCK-main_table.free_count--];
e->src=src;
e->sprt=sprt;
e->dst=dst;
e->dprt=dprt;
e->mark=mark;
e->count=count;
e->proto=proto;
e->hooknum=hooknum;
// e->next already zeroed by add_heap_blk
return e;
}
inline __u64 lost(__u32 count) { return main_table.lost += count;}
static __u64 new_packet(__u32 src, __u16 sprt, __u32 dst, __u16 dprt, __u32 mark,__u32 count, __u8 proto, __u8 hooknum)
{
int h;
struct acc_entry *e;
if( main_table.hash == 0)
return lost(count);
h=hash_func_acc(src,dst,mark);
for(e = main_table.hash[h]; e ;e=e->next )
if( e->src == src && e->sprt==sprt && e->dst == dst && e->dprt==dprt && e->mark == mark && e->proto == proto && e->hooknum == hooknum)
return e->count += count;
else if( e->next == 0 ) {
if( (e->next=new_entry(src,sprt, dst, dprt, mark,count,proto,hooknum)) == 0)
return lost(count);
return 0;
}
if( (main_table.hash[h] = new_entry(src,sprt,dst, dprt,mark,count,proto,hooknum)) == 0)
return lost(count);
return 0;
}
static int set_acc_ctl(struct sock *sk, int cmd, void *user, unsigned int len)
{
struct ip_acc_set_rq req;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd != SO_IP_ACC)
return -EBADF;
if (len != sizeof(req))
return -EINVAL;
if (copy_from_user(&req, user, sizeof(req)) != 0)
return -EFAULT;
if ( req.op & IP_ACC_SET_HASH )
switch ( req.hash_size ) {
case 1:
hash_size = 4096;
hash_module = 1021;
break;
case 4:
hash_size = 16384;
hash_module = 4093;
break;
case 16:
hash_size = 65536;
hash_module = 16381;
break;
default:
return -EINVAL;
}
if( req.op & IP_ACC_SET_LIMIT ) {
if( req.mem_limit >= 16 && req.mem_limit <= 1024 )
mem_limit = req.mem_limit;
else
return -EINVAL;
}
if( req.op & IP_ACC_SET_FLUSH )
backup_table();
return 0;
}
static int get_acc_ctl(struct sock *sk, int cmd, void *user, int *len)
{
struct acc_block *blk;
int nblk;
struct ip_acc_get_info info;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (cmd == SO_IP_ACC_INFO) {
if(*len != sizeof(info))
return -EINVAL;
info.blocks = back_table.blocks_count;
memcpy(&info.time_on,&back_table.time_on,sizeof(struct timeval));
memcpy(&info.time_off,&back_table.time_off,sizeof(struct timeval));
info.lost = back_table.lost;
if (copy_to_user(user,&info,sizeof(info)) != 0)
return -EFAULT;
return 0;
}
else if (cmd == SO_IP_ACC_BLOCK) {
if(*len != sizeof(struct acc_block))
return -EINVAL;
copy_from_user(&nblk,user,sizeof(int));
if( nblk < 0 || nblk >= back_table.blocks_count )
return -EINVAL;
for( blk = back_table.heap; blk ; blk=blk->next,nblk-- ) {
if( nblk == 0 ) {
copy_to_user(user,blk,sizeof(struct acc_block));
return 0;
}
}
return -EINVAL;
}
return -EBADF;
}
static unsigned int
target(struct sk_buff **pskb,
unsigned int hooknum,
const struct net_device *in,
const struct net_device *out,
const void *targinfo,
void *userinfo)
{
const struct ipt_acc_target_info *accinfo = targinfo;
struct iphdr *iph = (*pskb)->nh.iph;
struct icmphdr *icmp_hdr;
// struct igmphdr *igmp_hdr;
struct tcphdr *tcp_hdr;
struct udphdr *udp_hdr;
__u16 sport=0;
__u16 dport=0;
//FIXME: Rewrite this uglu pointers to smart and pretty new one :)
switch(iph->protocol)
{
case IPPROTO_ICMP:
icmp_hdr = (*pskb)->h.icmph;
sport=icmp_hdr->type;
dport=icmp_hdr->code;
break;
case IPPROTO_IGMP:
//FIXME: Add normal IGMP headers processing. ICMP has almost the same, so it works.
icmp_hdr = (*pskb)->h.icmph;
sport=icmp_hdr->type;
dport=icmp_hdr->code;
break;
case IPPROTO_TCP:
tcp_hdr = (*pskb)->h.th;
//We don't do ntohs() here. In userspace we trust. So Don't forget to modify ipacc.c
sport=tcp_hdr->source;
dport=tcp_hdr->dest;
break;
case IPPROTO_UDP:
udp_hdr = (*pskb)->h.uh;
sport=udp_hdr->source;
dport=udp_hdr->dest;
break;
default : break; //If we don't know portnumbers for that proto, it's 0 :)
}
printk("%d: %d -> %d || %d -> %d \n",hooknum, sport, dport, ntohs(sport), ntohs(dport));
WRITE_LOCK(&acc_lock);
new_packet(iph->saddr, sport, iph->daddr, dport ,accinfo->mark,ntohs(iph->tot_len), iph->protocol, hooknum);
WRITE_UNLOCK(&acc_lock);
return IPT_CONTINUE;
}
static int
checkentry(const char *tablename,
const struct ipt_entry *e,
void *targinfo,
unsigned int targinfosize,
unsigned int hook_mask)
{
if (targinfosize != IPT_ALIGN(sizeof(struct ipt_acc_target_info))) {
printk(KERN_WARNING "ACC: targinfosize %u != %Zu\n",
targinfosize,
IPT_ALIGN(sizeof(struct ipt_acc_target_info)));
return 0;
}
return 1;
}
static struct ipt_target ipt_acc_reg
= { { NULL, NULL }, "ACC", target, checkentry, NULL, THIS_MODULE };
static struct nf_sockopt_ops so_acc
= { { NULL, NULL }, PF_INET,
SO_IP_ACC, SO_IP_ACC+1, &set_acc_ctl,
SO_IP_ACC, SO_IP_ACC+2, &get_acc_ctl,
0, NULL };
static int __init init(void)
{
struct timeval t;
if (ipt_register_target(&ipt_acc_reg))
return -EINVAL;
if( nf_register_sockopt(&so_acc) ) {
ipt_unregister_target(&ipt_acc_reg);
return -EINVAL;
}
memset(&back_table,0,sizeof(struct acc_ctrl));
init_table(&main_table);
do_gettimeofday(&t);
memcpy(&main_table.time_on,&t,sizeof(struct timeval));
return 0;
}
static void __exit fini(void)
{
free_table(&main_table);
free_table(&back_table);
nf_unregister_sockopt(&so_acc);
ipt_unregister_target(&ipt_acc_reg);
}
module_init(init);
module_exit(fini);
[-- Attachment #4: ipt_ACC.h --]
[-- Type: text/plain, Size: 1438 bytes --]
#ifndef _IPT_ACC_H_target
#define _IPT_ACC_H_target
#define SO_IP_ACC 82
#define SO_IP_ACC_INFO SO_IP_ACC
#define SO_IP_ACC_BLOCK SO_IP_ACC_INFO+1
#define IP_ACC_SET_NONE 0
#define IP_ACC_SET_FLUSH 1
#define IP_ACC_SET_HASH 2
#define IP_ACC_SET_LIMIT 4
#define IP_ACC_GET_LIST 8
#define ACC_BLK_SZ 4096
#define ACC_HASH_SZ 4096
#define ACC_HEAP_BLK_SZ 4096
#define ACC_ENTRIES_PER_BLOCK 170
#define ACC_HASH_MOD 1021
struct acc_entry
{
__u32 src;
__u16 sprt;
__u32 dst;
__u16 dprt;
__u8 proto;
__u32 mark;
__u64 count;
__u8 hooknum;
// char dev[IFNAMSIZ]; // That's how we can add interface support
// Netfilter hooks are done similary, but mark is more flexible and doesn't require to write code :)
struct acc_entry *next;
};
struct acc_block
{
struct acc_block *next;
int fill[3];
struct acc_entry bl[ACC_ENTRIES_PER_BLOCK];
};
struct acc_ctrl
{
__u32 hash_size;
__u32 hash_module;
__u64 lost;
__u32 blocks_count;
__u32 free_count;
struct timeval time_on;
struct timeval time_off;
struct acc_entry **hash;
struct acc_block *heap;
struct acc_block *last_heap;
};
struct ip_acc_set_rq {
int op;
int hash_size;
int mem_limit;
};
struct ip_acc_get_info {
struct timeval time_on;
struct timeval time_off;
__u32 blocks;
__u64 lost;
};
struct ip_acc_get_block {
int block;
int fill[3];
struct acc_entry bl[170];
};
struct ipt_acc_target_info {
unsigned long mark;
};
#endif /*_IPT_ACC_H_target*/
[-- Attachment #5: libipt_ACC.c --]
[-- Type: text/plain, Size: 2394 bytes --]
#include <stdio.h>
#include <asm/types.h>
#include <stdlib.h>
#include <getopt.h>
#include <iptables.h>
#include <linux/netfilter_ipv4/ip_tables.h>
#include <linux/netfilter_ipv4/ipt_ACC.h>
struct accinfo {
struct ipt_entry_target t;
struct ipt_acc_target_info mark;
};
/* Function which prints out usage message. */
static void
help(void)
{
printf(
"ACC target v%s options:\n"
" --mark value Set mark value\n"
"\n",
IPTABLES_VERSION);
}
static struct option opts[] = {
{ "mark", 2, 0, '1' },
{ 0 }
};
/* Initialize the target. */
static void
init(struct ipt_entry_target *t, unsigned int *nfcache)
{
}
/* Function which parses command options; returns true if it
ate an option */
static int
parse(int c, char **argv, int invert, unsigned int *flags,
const struct ipt_entry *entry,
struct ipt_entry_target **target)
{
struct ipt_acc_target_info *accinfo
= (struct ipt_acc_target_info *)(*target)->data;
char *end;
if( c == '1' ) {
accinfo->mark = strtoul(optarg, &end, 0);
if (*end != '\0' || end == optarg)
exit_error(PARAMETER_PROBLEM, "Bad mark value `%s'", optarg);
if (*flags)
exit_error(PARAMETER_PROBLEM,
"ACC target: Can't specify --mark twice");
*flags = 1;
return 1;
}
return 0;
}
static void
final_check(unsigned int flags)
{
if (!flags)
exit_error(PARAMETER_PROBLEM,
"ACC target: Parameter --mark is required");
}
static void
print_mark(unsigned long mark, int numeric)
{
printf("0x%lx ", mark);
}
/* Prints out the targinfo. */
static void
print(const struct ipt_ip *ip,
const struct ipt_entry_target *target,
int numeric)
{
const struct ipt_acc_target_info *accinfo =
(const struct ipt_acc_target_info *)target->data;
printf("MARK set ");
print_mark(accinfo->mark, numeric);
}
/* Saves the union ipt_targinfo in parsable form to stdout. */
static void
save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
{
const struct ipt_acc_target_info *accinfo =
(const struct ipt_acc_target_info *)target->data;
printf("--mark 0x%lx ", accinfo->mark);
}
struct iptables_target acc
= { NULL,
"ACC",
IPTABLES_VERSION,
IPT_ALIGN(sizeof(struct ipt_acc_target_info)),
IPT_ALIGN(sizeof(struct ipt_acc_target_info)),
&help,
&init,
&parse,
&final_check,
&print,
&save,
opts
};
void _init(void)
{
register_target(&acc);
}
next reply other threads:[~2003-11-26 9:17 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2003-11-26 9:17 Stanisalv V. Bogatyrev [this message]
2003-11-26 10:46 ` ACC and port numers KOVACS Krisztian
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3FC46FA4.6060503@kenjitsu.net \
--to=s.bogatyrev@kenjitsu.net \
--cc=netfilter-devel@lists.netfilter.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.