/* 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 ? ip_hdr(skb)->saddr : ip_hdr(skb)->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_header(skb) >= skb->head && (skb_mac_header(skb) + 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, 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 ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr); if (!(skb_mac_header(skb) >= skb->head && (skb_mac_header(skb) + 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 = 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 ? ip_hdr(skb)->saddr : ip_hdr(skb)->daddr), 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);