From 633b62df0104203591d5c427f6769857571b2540 Mon Sep 17 00:00:00 2001 From: "/C=EU/ST=EU/CN=Jozsef Kadlecsik/emailAddress=kadlec@blackhole.kfki.hu" Date: Wed, 2 Jul 2008 12:20:18 +0000 Subject: Initial ipset release with kernel modules included. --- kernel/ip_set_macipmap.c | 360 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 360 insertions(+) create mode 100644 kernel/ip_set_macipmap.c (limited to 'kernel/ip_set_macipmap.c') diff --git a/kernel/ip_set_macipmap.c b/kernel/ip_set_macipmap.c new file mode 100644 index 0000000..e29c99d --- /dev/null +++ b/kernel/ip_set_macipmap.c @@ -0,0 +1,360 @@ +/* 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 +#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 = set->data; + struct ip_set_macip *table = map->members; + const struct ip_set_req_macipmap *req = 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 = set->data; + struct ip_set_macip *table = map->members; + ip_set_ip_t ip; + + ip = ntohl(flags[index] & IPSET_SRC +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + ? ip_hdr(skb)->saddr + : ip_hdr(skb)->daddr); +#else + ? skb->nh.iph->saddr + : skb->nh.iph->daddr); +#endif + + 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... */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + return (skb_mac_header(skb) >= skb->head + && (skb_mac_header(skb) + ETH_HLEN) <= skb->data +#else + return (skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data +#endif + && (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, const unsigned char *ethernet, ip_set_ip_t *hash_ip) +{ + struct ip_set_macipmap *map = set->data; + struct ip_set_macip *table = 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) +{ + const struct ip_set_req_macipmap *req = 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 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + ? ip_hdr(skb)->saddr + : ip_hdr(skb)->daddr); +#else + ? skb->nh.iph->saddr + : skb->nh.iph->daddr); +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + if (!(skb_mac_header(skb) >= skb->head + && (skb_mac_header(skb) + ETH_HLEN) <= skb->data)) +#else + if (!(skb->mac.raw >= skb->head + && (skb->mac.raw + ETH_HLEN) <= skb->data)) +#endif + 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 = set->data; + struct ip_set_macip *table = 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) +{ + const struct ip_set_req_macipmap *req = 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 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22) + ? ip_hdr(skb)->saddr + : ip_hdr(skb)->daddr), +#else + ? skb->nh.iph->saddr + : skb->nh.iph->daddr), +#endif + hash_ip); +} + +static inline size_t members_size(ip_set_ip_t from, ip_set_ip_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) +{ + size_t newbytes; + const struct ip_set_req_macipmap_create *req = 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 = 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 = 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) +{ + const struct ip_set_macipmap *map = set->data; + struct ip_set_req_macipmap_create *header = 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) +{ + const struct ip_set_macipmap *map = 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) +{ + const struct ip_set_macipmap *map = 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 ip_set_macipmap_init(void) +{ + init_max_page_size(); + return ip_set_register_set_type(&ip_set_macipmap); +} + +static void __exit ip_set_macipmap_fini(void) +{ + /* FIXME: possible race with ip_set_create() */ + ip_set_unregister_set_type(&ip_set_macipmap); +} + +module_init(ip_set_macipmap_init); +module_exit(ip_set_macipmap_fini); -- cgit v1.2.3