From 7b503a12a039aeb129fb75804824f6b365adb54b Mon Sep 17 00:00:00 2001 From: Adrian Ban Date: Tue, 8 May 2012 23:17:53 +0200 Subject: u32: example/test code for u32 hashing with HTB --- tests/test-complex-HTB-with-hash-filters.c | 753 +++++++++++++++++++++++++++++ 1 file changed, 753 insertions(+) create mode 100644 tests/test-complex-HTB-with-hash-filters.c (limited to 'tests/test-complex-HTB-with-hash-filters.c') diff --git a/tests/test-complex-HTB-with-hash-filters.c b/tests/test-complex-HTB-with-hash-filters.c new file mode 100644 index 00000000..585a549e --- /dev/null +++ b/tests/test-complex-HTB-with-hash-filters.c @@ -0,0 +1,753 @@ +/* + * test/test-complex-HTB-with-hash-filters.c Add HTB qdisc, HTB classes and creates some hash filters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2011 Adrian Ban + */ + +#include +#include +#include +#include +#include + +#include +//#include "include/rtnl_u32.h" + +#include +#include +//#include "include/rtnl_u32_addon.h" + +#define TC_HANDLE(maj, min) (TC_H_MAJ((maj) << 16) | TC_H_MIN(min)) + +/* some functions are copied from iproute-tc tool */ +int get_u32(__u32 *val, const char *arg, int base) +{ + unsigned long res; + char *ptr; + + if (!arg || !*arg) + return -1; + res = strtoul(arg, &ptr, base); + if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL) + return -1; + *val = res; + return 0; +} + +int get_u32_handle(__u32 *handle, const char *str) +{ + __u32 htid=0, hash=0, nodeid=0; + char *tmp = strchr(str, ':'); + + if (tmp == NULL) { + if (memcmp("0x", str, 2) == 0) + return get_u32(handle, str, 16); + return -1; + } + htid = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (htid>=0x1000) + return -1; + if (*tmp) { + str = tmp+1; + hash = strtoul(str, &tmp, 16); + if (tmp == str && *str != ':' && *str != 0) + return -1; + if (hash>=0x100) + return -1; + if (*tmp) { + str = tmp+1; + nodeid = strtoul(str, &tmp, 16); + if (tmp == str && *str != 0) + return -1; + if (nodeid>=0x1000) + return -1; + } + } + *handle = (htid<<20)|(hash<<12)|nodeid; + return 0; +} + +uint32_t get_u32_parse_handle(const char *cHandle) +{ + uint32_t handle=0; + + if(get_u32_handle(&handle, cHandle)) { + printf ("Illegal \"ht\"\n"); + return -1; + } + + if (handle && TC_U32_NODE(handle)) { + printf("\"link\" must be a hash table.\n"); + return -1; + } + return handle; +} + +int get_tc_classid(__u32 *h, const char *str) +{ + __u32 maj, min; + char *p; + + maj = TC_H_ROOT; + if (strcmp(str, "root") == 0) + goto ok; + maj = TC_H_UNSPEC; + if (strcmp(str, "none") == 0) + goto ok; + maj = strtoul(str, &p, 16); + if (p == str) { + maj = 0; + if (*p != ':') + return -1; + } + if (*p == ':') { + if (maj >= (1<<16)) + return -1; + maj <<= 16; + str = p+1; + min = strtoul(str, &p, 16); + if (*p != 0) + return -1; + if (min >= (1<<16)) + return -1; + maj |= min; + } else if (*p != 0) + return -1; + +ok: + *h = maj; + return 0; +} + +/* + * Function that adds a new filter and attach it to a hash table + * + */ +int u32_add_filter_on_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t classid +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if (err = rtnl_tc_set_kind(TC_CAST(cls), "u32")) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); /* 10.0.0.0/8 */ + + rtnl_u32_set_classid(cls, classid); + + rtnl_u32_set_cls_terminal(cls); + + if (err = rtnl_cls_add(sock, cls, NLM_F_CREATE)) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; + +} + +/* + * Function that adds a new filter and attach it to a hash table + * and set next hash table link with hash mask + * + */ +int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, + uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask, + uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset +) +{ + struct rtnl_cls *cls; + int err; + + //printf("Key Val : 0x%x\n", keyval); + //printf("Key Mask : 0x%x\n", keymask); + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if (err = rtnl_tc_set_kind(TC_CAST(cls), "u32")) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + if (htid) + rtnl_u32_set_hashtable(cls, htid); + + rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask); + + rtnl_u32_set_hashmask(cls, hmask, hoffset); + + rtnl_u32_set_link(cls, htlink); + + + if (err = rtnl_cls_add(sock, cls, NLM_F_CREATE)) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that creates a new hash table + */ +int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor) +{ + + int err; + struct rtnl_cls *cls; + + cls=rtnl_cls_alloc(); + if (!(cls)) { + printf("Can not allocate classifier\n"); + nl_socket_free(sock); + exit(1); + } + + rtnl_tc_set_link(TC_CAST(cls), rtnlLink); + + if (err = rtnl_tc_set_kind(TC_CAST(cls), "u32")) { + printf("Can not set classifier as u32\n"); + return 1; + } + + rtnl_cls_set_prio(cls, prio); + rtnl_cls_set_protocol(cls, ETH_P_IP); + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(1, 0)); + + rtnl_u32_set_handle(cls, htid, 0x0, 0x0); + //printf("htid: 0x%X\n", htid); + rtnl_u32_set_divisor(cls, divisor); + + if (err = rtnl_cls_add(sock, cls, NLM_F_CREATE)) { + printf("Can not add classifier: %s\n", nl_geterror(err)); + return -1; + } + rtnl_cls_put(cls); + return 0; +} + +/* + * function that adds a new HTB qdisc and set the default class for unclassified traffic + */ +int qdisc_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t defaultClass) +{ + + struct rtnl_qdisc *qdisc; + int err; + + /* Allocation of a qdisc object */ + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate Qdisc\n"); + return -1; + } + + //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index); + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT); + + //delete the qdisc + //printf("Delete current qdisc\n"); + rtnl_qdisc_delete(sock, qdisc); + //rtnl_qdisc_put(qdisc); + + //add a HTB qdisc + //printf("Add a new HTB qdisc\n"); + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(1,0)); + + if (err = rtnl_tc_set_kind(TC_CAST(qdisc), "htb") ){ + printf("Can not allocate HTB\n"); + return -1; + } + + /* Set default class for unclassified traffic */ + //printf("Set default class for unclassified traffic\n"); + rtnl_htb_set_defcls(qdisc, TC_HANDLE(1, defaultClass)); + rtnl_htb_set_rate2quantum(qdisc, 1); + + /* Submit request to kernel and wait for response */ + if (err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE)) { + printf("Can not allocate HTB Qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); + +} + +/* + * function that adds a new HTB class and set its parameters + */ +int class_add_HTB(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + uint32_t childMaj, uint32_t childMin, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst, + uint32_t prio +) +{ + int err; + struct rtnl_class *class; + + //create a HTB class + class = (struct rtnl_class *)rtnl_class_alloc(); + //class = rtnl_class_alloc(); + if (!class) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + //add a HTB qdisc + //printf("Add a new HTB class with 0x%X:0x%X on parent 0x%X:0x%X\n", childMaj, childMin, parentMaj, parentMin); + rtnl_tc_set_parent(TC_CAST(class), TC_HANDLE(parentMaj, parentMin)); + rtnl_tc_set_handle(TC_CAST(class), TC_HANDLE(childMaj, childMin)); + + if (err = rtnl_tc_set_kind(TC_CAST(class), "htb") ){ + printf("Can not set HTB to class\n"); + return 1; + } + + //printf("set HTB class prio to %u\n", prio); + rtnl_htb_set_prio(class, prio); + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + //printf ("Class HTB: set rate burst: %u\n", burst); + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + //printf ("Class HTB: set rate cburst: %u\n", cburst); + rtnl_htb_set_cbuffer(class, cburst); + } + /* Submit request to kernel and wait for response */ + if (err = rtnl_class_add(sock, class, NLM_F_CREATE)) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + +} + +/* + * function that adds a HTB root class and set its parameters + */ +int class_add_HTB_root(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint64_t rate, uint64_t ceil, + uint32_t burst, uint32_t cburst +) +{ + int err; + struct rtnl_class *class; + + //create a HTB class + class = (struct rtnl_class *)rtnl_class_alloc(); + //class = rtnl_class_alloc(); + if (!class) { + printf("Can not allocate class object\n"); + return 1; + } + // + rtnl_tc_set_link(TC_CAST(class), rtnlLink); + rtnl_tc_set_parent(TC_CAST(class), TC_H_ROOT); + //add a HTB class + //printf("Add a new HTB ROOT class\n"); + rtnl_tc_set_handle(TC_CAST(class), 1); + + if (err = rtnl_tc_set_kind(TC_CAST(class), "htb") ){ + printf("Can not set HTB to class\n"); + return 1; + } + + if (rate) { + //rate=rate/8; + rtnl_htb_set_rate(class, rate); + } + if (ceil) { + //ceil=ceil/8; + rtnl_htb_set_ceil(class, ceil); + } + + if (burst) { + rtnl_htb_set_rbuffer(class, burst); + } + if (cburst) { + rtnl_htb_set_cbuffer(class, cburst); + } + + /* Submit request to kernel and wait for response */ + if (err = rtnl_class_add(sock, class, NLM_F_CREATE)) { + printf("Can not allocate HTB Qdisc\n"); + return 1; + } + rtnl_class_put(class); + +} + +/* + * function that adds a new SFQ qdisc as a leaf for a HTB class + */ +int qdisc_add_SFQ_leaf(struct nl_sock *sock, struct rtnl_link *rtnlLink, + uint32_t parentMaj, uint32_t parentMin, + int quantum, int limit, int perturb +) +{ + int err; + struct rtnl_qdisc *qdisc; + + if (!(qdisc = rtnl_qdisc_alloc())) { + printf("Can not allocate qdisc object\n"); + return 1; + } + rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink); + rtnl_tc_set_parent(TC_CAST(qdisc), TC_HANDLE(parentMaj, parentMin)); + + rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(parentMin,0)); + + if (err = rtnl_tc_set_kind(TC_CAST(qdisc), "sfq") ){ + printf("Can not set SQF class\n"); + return 1; + } + + if(quantum) { + rtnl_sfq_set_quantum(qdisc, quantum); + } else { + rtnl_sfq_set_quantum(qdisc, 16000); // tc default value + } + if(limit) { + rtnl_sfq_set_limit(qdisc, limit); // default is 127 + } + if(perturb) { + rtnl_sfq_set_perturb(qdisc, perturb); // default never perturb the hash + } + + /* Submit request to kernel and wait for response */ + if (err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE)) { + printf("Can not allocate SFQ qdisc\n"); + return -1; + } + + /* Return the qdisc object to free memory resources */ + rtnl_qdisc_put(qdisc); +} + + + + +int main() { + + struct nl_sock *sock; + struct rtnl_link *link; + + struct rtnl_qdisc *qdisc; + struct rtnl_class *class; + struct rtnl_cls *cls; + + uint32_t ht, htlink, htid, handle, divisor, hash, nodeid, hashmask, direction, classid; + struct rtnl_u32 *f_u32; + char chashlink[16]=""; + + uint64_t drops, qlen; + + int master_index; + int err; + + uint64_t rate=0, ceil=0; + + struct nl_cache *link_cache; + + if (!(sock = nl_socket_alloc())) { + printf("Unable to allocate netlink socket\n"); + exit(1); + } + + if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) { + printf("Nu s-a putut conecta la NETLINK!\n"); + nl_socket_free(sock); + exit(1); + } + + + if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) { + printf("Unable to allocate link cache: %s\n", + nl_geterror(err)); + nl_socket_free(sock); + exit(1); + } + + /* lookup interface index of eth0 */ + if (!(link = rtnl_link_get_by_name(link_cache, "eth0"))) { + /* error */ + printf("Interface not found\n"); + nl_socket_free(sock); + exit(1); + } + + err=qdisc_add_HTB(sock, link, 0xffff); + //drops = rtnl_tc_get_stat(TC_CAST(qdisc), RTNL_TC_DROPS); + + //printf("Add ROOT HTB class\n"); + err=class_add_HTB_root(sock, link, 12500000, 12500000, 25000, 25000); + err=class_add_HTB(sock, link, 1, 0, 1, 0xffff, 1250000, 12500000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0xffff, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x5, 2000000, 2000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x5, 16000, 0, 10); + err=class_add_HTB(sock, link, 1, 1, 1, 0x6, 1000000, 1000000, 25000, 25000, 5); + err=qdisc_add_SFQ_leaf(sock, link, 1, 0x6, 16000, 0, 10); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x7, 1024000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x8, 2048000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0x9, 4096000, 100000000, 5); + //err=class_add_HTB(sock, link, 1, 0, 1, 0xa, 8192000, 100000000, 5); + + //printf("Add main hash table\n"); + + /* create u32 first hash filter table + * + */ + /* formula calcul handle: + * uint32_t handle = (htid << 20) | (hash << 12) | nodeid; + */ + + /* + * Upper limit of number of hash tables: 4096 (0xFFF) + * Number of hashes in a table: 256 values (0xFF) + * + */ + + /* using 256 values for hash table + * each entry in hash table match a byte from IP address specified later by a hash key + */ + + uint32_t i; + for (i = 1; i <= 0xf; i++) + u32_add_ht(sock, link, 1, i, 256); + + /* + * attach a u32 filter to the first hash + * that redirects all traffic and make a hash key + * from the fist byte of the IP address + * + */ + + divisor=0x0; // unused here + handle = 0x0; // unused here + hash = 0x0; // unused here + htid = 0x0; // unused here + nodeid = 0x0; // unused here + uint32_t hashlink; + + direction = 16; + + /* + * which hash table will use + * in our case is hash table no 1 defined previous + * + * There are 2 posibilities to set the the hash table: + * 1. Using function get_u32_handle and sent a string in + * format 10: where 10 is number of the hash table + * 2. Create your own value in format: 0xa00000 + * + */ + strcpy(chashlink, "1:"); + //printf("Hash Link: %s\n", chashlink); + //chashlink=malloc(sizeof(char) * + htlink = 0x0; // is used by get_u32_handle to return the correct value of hash table (link) + + if(get_u32_handle(&htlink, chashlink)) { + printf ("Illegal \"link\""); + nl_socket_free(sock); + exit(1); + } + //printf ("hash link : 0x%X\n", htlink); + //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink))); + + if (htlink && TC_U32_NODE(htlink)) { + printf("\"link\" must be a hash table.\n"); + nl_socket_free(sock); + exit(1); + } + /* the hash mask will hit the hash table (link) no 1: in our case + */ + + /* set the hash key mask */ + //hashmask = 0xFF000000UL; // the mask that is used to match the hash in specific table, in our case for example 1:a with mean the first byte which is 10 in hash table 1 + + /* Here we add a hash filter which match the first byte (see the hashmask value) + * of the source IP (offset 12 in the packet header) + * You can use also offset 16 to match the destination IP + */ + + /* + * Also we need a filter to match our rule + * This mean that we will put a 0.0.0.0/0 filter in our first rule + * that match the offset 12 (source IP) + * Also you can put offset 16 to match the destination IP + */ + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0, 0x0, direction, 0, + 0, htlink, 0xff000000, direction); + + /* + * For each first byte that we need to match we will create a new hash table + * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23 + * For byte 10 and byte 172 will create a separate hash table that will match the second + * byte from each class. + * + */ + + + // Create a new hash table with prio 1, id 2 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 2, 256); + // Create a new hash table with prio 1, id 3 and 256 entries +// u32_CreateNewHashTable(sock, link, 1, 3, 256); +// u32_CreateNewHashTable(sock, link, 1, 4, 256); +// u32_CreateNewHashTable(sock, link, 1, 5, 256); + + /* + * Now we will create other filter under (ATENTION) our first hash table (link) 1: + * Previous rule redirects the trafic according the hash mask to hash table (link) no 1: + * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach + * other rules that matches next byte from IP source/destination IP and we will repeat the + * previous steps. + * + */ + + + // /8 check + + // 10.0.0.0/8 + ht=get_u32_parse_handle("1:a:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("2:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + // 172.0.0.0/8 + ht=get_u32_parse_handle("1:ac:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("3:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac000000, 0xff000000, direction, 0, + htid, htlink, 0x00ff0000, direction); + + + // /16 check + // 10.0.0.0/16 + ht=get_u32_parse_handle("2:0:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("4:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // 172.17.0.0/16 + ht=get_u32_parse_handle("3:11:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("5:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110000, 0xffff0000, direction, 0, + htid, htlink, 0x0000ff00, direction); + + // /24 check + // 10.0.9.0/24 + ht=get_u32_parse_handle("4:9:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("6:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0x0a000900, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + // 172.17.2.0/16 + ht=get_u32_parse_handle("5:2:"); + htid = (ht&0xFFFFF000); + htlink=get_u32_parse_handle("7:"); + + u32_add_filter_on_ht_with_hashmask(sock, link, 1, + 0xac110200, 0xffffff00, direction, 0, + htid, htlink, 0x000000ff, direction); + + + // final filters + // 10.0.9.20 + ht=get_u32_parse_handle("6:14:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:5"); + + u32_add_filter_on_ht(sock, link, 1, + 0x0a000914, 0xffffffff, direction, 0, + htid, classid); + + // 172.17.2.120 + ht=get_u32_parse_handle("7:78:"); + htid = (ht&0xFFFFF000); + + err = get_tc_classid(&classid, "1:6"); + + u32_add_filter_on_ht(sock, link, 1, + 0xac110278, 0xffffffff, direction, 0, + htid, classid); + + + + nl_socket_free(sock); + return 0; +} -- cgit v1.2.3