* ACC and port numers
@ 2003-11-26 9:17 Stanisalv V. Bogatyrev
2003-11-26 10:46 ` KOVACS Krisztian
0 siblings, 1 reply; 2+ messages in thread
From: Stanisalv V. Bogatyrev @ 2003-11-26 9:17 UTC (permalink / raw)
To: netfilter-devel
[-- 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);
}
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: ACC and port numers
2003-11-26 9:17 ACC and port numers Stanisalv V. Bogatyrev
@ 2003-11-26 10:46 ` KOVACS Krisztian
0 siblings, 0 replies; 2+ messages in thread
From: KOVACS Krisztian @ 2003-11-26 10:46 UTC (permalink / raw)
To: s.bogatyrev; +Cc: netfilter-devel
Hi,
Stanisalv V. Bogatyrev wrote:
> 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?
> 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;
The problem lies here: for incoming packets, skb->h.th is not yet set.
You should try it this way:
struct iphdr *iph = (*pskb)->nh.iph;
struct tcphdr *tcp_hdr = (struct tcphdr *)((u_int32_t *)iph + iph->ihl);
Of course the code for UDP, ICMP, etc. should also be upgraded (for TCP
and UDP the ports are at the same location). I didn't check, but you
should also take care of byte order problems.
--
Regards,
Krisztian KOVACS
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2003-11-26 10:46 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2003-11-26 9:17 ACC and port numers Stanisalv V. Bogatyrev
2003-11-26 10:46 ` KOVACS Krisztian
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.