X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fipv4%2Fnetfilter%2Fip_set_macipmap.c;fp=net%2Fipv4%2Fnetfilter%2Fip_set_macipmap.c;h=0558e8c2b04229148e98ebf1013ebb8af13d37d1;hb=cee37fe97739d85991964371c1f3a745c00dd236;hp=0000000000000000000000000000000000000000;hpb=37b9e453a7750f55f473d51c91d184b703c2b883;p=linux-2.6.git diff --git a/net/ipv4/netfilter/ip_set_macipmap.c b/net/ipv4/netfilter/ip_set_macipmap.c new file mode 100644 index 000000000..0558e8c2b --- /dev/null +++ b/net/ipv4/netfilter/ip_set_macipmap.c @@ -0,0 +1,353 @@ +/* Copyright (C) 2000-2002 Joakim Axelsson + * Patrick Schaaf + * Martin Josefsson + * Copyright (C) 2003-2004 Jozsef Kadlecsik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Kernel module implementing an IP set type: the macipmap type */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static int +testip(struct ip_set *set, const void *data, size_t size, ip_set_ip_t *hash_ip) +{ + struct ip_set_macipmap *map = (struct ip_set_macipmap *) set->data; + struct ip_set_macip *table = (struct ip_set_macip *) map->members; + struct ip_set_req_macipmap *req = (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %zu, have %zu)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + + if (req->ip < map->first_ip || req->ip > map->last_ip) + return -ERANGE; + + *hash_ip = req->ip; + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", + set->name, HIPQUAD(req->ip), HIPQUAD(*hash_ip)); + if (test_bit(IPSET_MACIP_ISSET, + (void *) &table[req->ip - map->first_ip].flags)) { + return (memcmp(req->ethernet, + &table[req->ip - map->first_ip].ethernet, + ETH_ALEN) == 0); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +static int +testip_kernel(struct ip_set *set, + const struct sk_buff *skb, + ip_set_ip_t *hash_ip, + const u_int32_t *flags, + unsigned char index) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + ip_set_ip_t ip; + + ip = ntohl(flags[index] & IPSET_SRC + ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + DP("flag: %s src: %u.%u.%u.%u dst: %u.%u.%u.%u", + flags[index] & IPSET_SRC ? "SRC" : "DST", + NIPQUAD(skb->nh.iph->saddr), + NIPQUAD(skb->nh.iph->daddr)); + + if (ip < map->first_ip || ip > map->last_ip) + return 0; + + *hash_ip = ip; + DP("set: %s, ip:%u.%u.%u.%u, %u.%u.%u.%u", + set->name, HIPQUAD(ip), HIPQUAD(*hash_ip)); + if (test_bit(IPSET_MACIP_ISSET, + (void *) &table[ip - map->first_ip].flags)) { + /* Is mac pointer valid? + * If so, compare... */ + return (skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data + && (memcmp(eth_hdr(skb)->h_source, + &table[ip - map->first_ip].ethernet, + ETH_ALEN) == 0)); + } else { + return (map->flags & IPSET_MACIP_MATCHUNSET ? 1 : 0); + } +} + +/* returns 0 on success */ +static inline int +__addip(struct ip_set *set, + ip_set_ip_t ip, unsigned char *ethernet, ip_set_ip_t *hash_ip) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (test_and_set_bit(IPSET_MACIP_ISSET, + (void *) &table[ip - map->first_ip].flags)) + return -EEXIST; + + *hash_ip = ip; + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); + memcpy(&table[ip - map->first_ip].ethernet, ethernet, ETH_ALEN); + return 0; +} + +static int +addip(struct ip_set *set, const void *data, size_t size, + ip_set_ip_t *hash_ip) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %zu, have %zu)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __addip(set, req->ip, req->ethernet, hash_ip); +} + +static int +addip_kernel(struct ip_set *set, + const struct sk_buff *skb, + ip_set_ip_t *hash_ip, + const u_int32_t *flags, + unsigned char index) +{ + ip_set_ip_t ip; + + ip = ntohl(flags[index] & IPSET_SRC + ? skb->nh.iph->saddr + : skb->nh.iph->daddr); + + if (!(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data)) + return -EINVAL; + + return __addip(set, ip, eth_hdr(skb)->h_source, hash_ip); +} + +static inline int +__delip(struct ip_set *set, ip_set_ip_t ip, ip_set_ip_t *hash_ip) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + struct ip_set_macip *table = + (struct ip_set_macip *) map->members; + + if (ip < map->first_ip || ip > map->last_ip) + return -ERANGE; + if (!test_and_clear_bit(IPSET_MACIP_ISSET, + (void *)&table[ip - map->first_ip].flags)) + return -EEXIST; + + *hash_ip = ip; + DP("%u.%u.%u.%u, %u.%u.%u.%u", HIPQUAD(ip), HIPQUAD(*hash_ip)); + return 0; +} + +static int +delip(struct ip_set *set, const void *data, size_t size, + ip_set_ip_t *hash_ip) +{ + struct ip_set_req_macipmap *req = + (struct ip_set_req_macipmap *) data; + + if (size != sizeof(struct ip_set_req_macipmap)) { + ip_set_printk("data length wrong (want %zu, have %zu)", + sizeof(struct ip_set_req_macipmap), + size); + return -EINVAL; + } + return __delip(set, req->ip, hash_ip); +} + +static int +delip_kernel(struct ip_set *set, + const struct sk_buff *skb, + ip_set_ip_t *hash_ip, + const u_int32_t *flags, + unsigned char index) +{ + return __delip(set, + ntohl(flags[index] & IPSET_SRC + ? skb->nh.iph->saddr + : skb->nh.iph->daddr), + hash_ip); +} + +static inline size_t members_size(ip_set_id_t from, ip_set_id_t to) +{ + return (size_t)((to - from + 1) * sizeof(struct ip_set_macip)); +} + +static int create(struct ip_set *set, const void *data, size_t size) +{ + int newbytes; + struct ip_set_req_macipmap_create *req = + (struct ip_set_req_macipmap_create *) data; + struct ip_set_macipmap *map; + + if (size != sizeof(struct ip_set_req_macipmap_create)) { + ip_set_printk("data length wrong (want %zu, have %zu)", + sizeof(struct ip_set_req_macipmap_create), + size); + return -EINVAL; + } + + DP("from %u.%u.%u.%u to %u.%u.%u.%u", + HIPQUAD(req->from), HIPQUAD(req->to)); + + if (req->from > req->to) { + DP("bad ip range"); + return -ENOEXEC; + } + + if (req->to - req->from > MAX_RANGE) { + ip_set_printk("range too big (max %d addresses)", + MAX_RANGE+1); + return -ENOEXEC; + } + + map = kmalloc(sizeof(struct ip_set_macipmap), GFP_KERNEL); + if (!map) { + DP("out of memory for %d bytes", + sizeof(struct ip_set_macipmap)); + return -ENOMEM; + } + map->flags = req->flags; + map->first_ip = req->from; + map->last_ip = req->to; + newbytes = members_size(map->first_ip, map->last_ip); + map->members = ip_set_malloc(newbytes); + DP("members: %u %p", newbytes, map->members); + if (!map->members) { + DP("out of memory for %d bytes", newbytes); + kfree(map); + return -ENOMEM; + } + memset(map->members, 0, newbytes); + + set->data = map; + return 0; +} + +static void destroy(struct ip_set *set) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + + ip_set_free(map->members, members_size(map->first_ip, map->last_ip)); + kfree(map); + + set->data = NULL; +} + +static void flush(struct ip_set *set) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + memset(map->members, 0, members_size(map->first_ip, map->last_ip)); +} + +static void list_header(const struct ip_set *set, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + struct ip_set_req_macipmap_create *header = + (struct ip_set_req_macipmap_create *) data; + + DP("list_header %x %x %u", map->first_ip, map->last_ip, + map->flags); + + header->from = map->first_ip; + header->to = map->last_ip; + header->flags = map->flags; +} + +static int list_members_size(const struct ip_set *set) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + + DP("%u", members_size(map->first_ip, map->last_ip)); + return members_size(map->first_ip, map->last_ip); +} + +static void list_members(const struct ip_set *set, void *data) +{ + struct ip_set_macipmap *map = + (struct ip_set_macipmap *) set->data; + + int bytes = members_size(map->first_ip, map->last_ip); + + DP("members: %u %p", bytes, map->members); + memcpy(data, map->members, bytes); +} + +static struct ip_set_type ip_set_macipmap = { + .typename = SETTYPE_NAME, + .features = IPSET_TYPE_IP | IPSET_DATA_SINGLE, + .protocol_version = IP_SET_PROTOCOL_VERSION, + .create = &create, + .destroy = &destroy, + .flush = &flush, + .reqsize = sizeof(struct ip_set_req_macipmap), + .addip = &addip, + .addip_kernel = &addip_kernel, + .delip = &delip, + .delip_kernel = &delip_kernel, + .testip = &testip, + .testip_kernel = &testip_kernel, + .header_size = sizeof(struct ip_set_req_macipmap_create), + .list_header = &list_header, + .list_members_size = &list_members_size, + .list_members = &list_members, + .me = THIS_MODULE, +}; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jozsef Kadlecsik "); +MODULE_DESCRIPTION("macipmap type of IP sets"); + +static int __init init(void) +{ + init_max_malloc_size(); + return ip_set_register_set_type(&ip_set_macipmap); +} + +static void __exit fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_macipmap); +} + +module_init(init); +module_exit(fini);