Home | History | Annotate | Download | only in tests
      1 /*
      2  * test/tests-u32-with-actions.c     Add ingress qdisc, create some hash filters, and add redirect action
      3  *
      4  *      This library is free software; you can redistribute it and/or
      5  *      modify it under the terms of the GNU Lesser General Public
      6  *      License as published by the Free Software Foundation version 2.1
      7  *      of the License.
      8  *
      9  * Stolen from tests/test-complex-HTB-with-hash-filters.c
     10  *
     11  * Copyright (c) 2013 Cong Wang <xiyou.wangcong (at) gmail.com>
     12  */
     13 
     14 #include <netlink/route/link.h>
     15 #include <netlink/route/tc.h>
     16 #include <netlink/route/qdisc.h>
     17 #include <netlink/route/cls/u32.h>
     18 #include <netlink/route/classifier.h>
     19 #include <netlink/route/action.h>
     20 #include <netlink/route/act/mirred.h>
     21 #include <netlink/route/class.h>
     22 #include <linux/if_ether.h>
     23 
     24 #include <netlink/attr.h>
     25 #include <stdio.h>
     26 #include <string.h>
     27 
     28 #define 	TC_HANDLE(maj, min)   (TC_H_MAJ((maj) << 16) | TC_H_MIN(min))
     29 
     30 /* some functions are copied from iproute-tc tool */
     31 static int get_u32(__u32 *val, const char *arg, int base)
     32 {
     33 	unsigned long res;
     34 	char *ptr;
     35 
     36 	if (!arg || !*arg)
     37 		return -1;
     38 	res = strtoul(arg, &ptr, base);
     39 	if (!ptr || ptr == arg || *ptr || res > 0xFFFFFFFFUL)
     40 		return -1;
     41 	*val = res;
     42 	return 0;
     43 }
     44 
     45 static int get_u32_handle(__u32 *handle, const char *str)
     46 {
     47 	__u32 htid=0, hash=0, nodeid=0;
     48 	char *tmp = strchr(str, ':');
     49 
     50 	if (tmp == NULL) {
     51 		if (memcmp("0x", str, 2) == 0)
     52 			return get_u32(handle, str, 16);
     53 		return -1;
     54 	}
     55 	htid = strtoul(str, &tmp, 16);
     56 	if (tmp == str && *str != ':' && *str != 0)
     57 		return -1;
     58 	if (htid>=0x1000)
     59 		return -1;
     60 	if (*tmp) {
     61 		str = tmp+1;
     62 		hash = strtoul(str, &tmp, 16);
     63 		if (tmp == str && *str != ':' && *str != 0)
     64 			return -1;
     65 		if (hash>=0x100)
     66 			return -1;
     67 		if (*tmp) {
     68 			str = tmp+1;
     69 			nodeid = strtoul(str, &tmp, 16);
     70 			if (tmp == str && *str != 0)
     71 				return -1;
     72 			if (nodeid>=0x1000)
     73 				return -1;
     74 		}
     75 	}
     76 	*handle = (htid<<20)|(hash<<12)|nodeid;
     77 	return 0;
     78 }
     79 
     80 static uint32_t get_u32_parse_handle(const char *cHandle)
     81 {
     82 	uint32_t handle=0;
     83 
     84 	if(get_u32_handle(&handle, cHandle)) {
     85 		printf ("Illegal \"ht\"\n");
     86 		return -1;
     87 	}
     88 
     89 	if (handle && TC_U32_NODE(handle)) {
     90 		printf("\"link\" must be a hash table.\n");
     91 		return -1;
     92 	}
     93 	return handle;
     94 }
     95 
     96 /*
     97  * Function that adds a new filter and attach it to a hash table
     98  * and set next hash table link with hash mask
     99  *
    100  */
    101 static
    102 int u32_add_filter_on_ht_with_hashmask(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio,
    103 	    uint32_t keyval, uint32_t keymask, int keyoff, int keyoffmask,
    104 	    uint32_t htid, uint32_t htlink, uint32_t hmask, uint32_t hoffset, struct rtnl_act *act)
    105 {
    106     struct rtnl_cls *cls;
    107     int err;
    108 
    109     cls=rtnl_cls_alloc();
    110     if (!(cls)) {
    111         printf("Can not allocate classifier\n");
    112         nl_socket_free(sock);
    113         exit(1);
    114     }
    115 
    116     rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
    117 
    118     if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
    119         printf("Can not set classifier as u32\n");
    120         return 1;
    121     }
    122 
    123     rtnl_cls_set_prio(cls, prio);
    124     rtnl_cls_set_protocol(cls, ETH_P_IP);
    125 
    126     rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0));
    127 
    128     if (htid)
    129 	rtnl_u32_set_hashtable(cls, htid);
    130 
    131     rtnl_u32_add_key_uint32(cls, keyval, keymask, keyoff, keyoffmask);
    132 
    133     rtnl_u32_set_hashmask(cls, hmask, hoffset);
    134 
    135     rtnl_u32_set_link(cls, htlink);
    136 
    137     rtnl_u32_add_action(cls, act);
    138 
    139 
    140     if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
    141         printf("Can not add classifier: %s\n", nl_geterror(err));
    142         return -1;
    143     }
    144     rtnl_cls_put(cls);
    145     return 0;
    146 }
    147 
    148 /*
    149  * function that creates a new hash table
    150  */
    151 static
    152 int u32_add_ht(struct nl_sock *sock, struct rtnl_link *rtnlLink, uint32_t prio, uint32_t htid, uint32_t divisor)
    153 {
    154 
    155     int err;
    156     struct rtnl_cls *cls;
    157 
    158     cls=rtnl_cls_alloc();
    159     if (!(cls)) {
    160         printf("Can not allocate classifier\n");
    161         nl_socket_free(sock);
    162         exit(1);
    163     }
    164 
    165     rtnl_tc_set_link(TC_CAST(cls), rtnlLink);
    166 
    167     if ((err = rtnl_tc_set_kind(TC_CAST(cls), "u32"))) {
    168         printf("Can not set classifier as u32\n");
    169         return 1;
    170     }
    171 
    172     rtnl_cls_set_prio(cls, prio);
    173     rtnl_cls_set_protocol(cls, ETH_P_IP);
    174     rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0));
    175 
    176     rtnl_u32_set_handle(cls, htid, 0x0, 0x0);
    177     //printf("htid: 0x%X\n", htid);
    178     rtnl_u32_set_divisor(cls, divisor);
    179 
    180     if ((err = rtnl_cls_add(sock, cls, NLM_F_CREATE))) {
    181         printf("Can not add classifier: %s\n", nl_geterror(err));
    182         return -1;
    183     }
    184     rtnl_cls_put(cls);
    185     return 0;
    186 }
    187 
    188 /*
    189  * function that adds a new ingress qdisc and set the default class for unclassified traffic
    190  */
    191 static
    192 int qdisc_add_ingress(struct nl_sock *sock, struct rtnl_link *rtnlLink)
    193 {
    194 
    195     struct rtnl_qdisc *qdisc;
    196     int err;
    197 
    198     /* Allocation of a qdisc object */
    199     if (!(qdisc = rtnl_qdisc_alloc())) {
    200         printf("Can not allocate Qdisc\n");
    201 	return -1;
    202     }
    203 
    204     //rtnl_tc_set_ifindex(TC_CAST(qdisc), master_index);
    205     rtnl_tc_set_link(TC_CAST(qdisc), rtnlLink);
    206     rtnl_tc_set_parent(TC_CAST(qdisc), TC_H_ROOT);
    207 
    208     //printf("Delete current qdisc\n");
    209     rtnl_qdisc_delete(sock, qdisc);
    210     //rtnl_qdisc_put(qdisc);
    211 
    212     rtnl_tc_set_handle(TC_CAST(qdisc), TC_HANDLE(0xffff, 0));
    213 
    214     if ((err = rtnl_tc_set_kind(TC_CAST(qdisc), "ingress"))) {
    215         printf("Can not allocate ingress\n");
    216 	return -1;
    217     }
    218 
    219     /* Submit request to kernel and wait for response */
    220     if ((err = rtnl_qdisc_add(sock, qdisc, NLM_F_CREATE))) {
    221         printf("Can not allocate ingress Qdisc\n");
    222 	return -1;
    223     }
    224 
    225     /* Return the qdisc object to free memory resources */
    226     rtnl_qdisc_put(qdisc);
    227 
    228     return 0;
    229 }
    230 
    231 int main(void)
    232 {
    233     struct nl_sock *sock;
    234     struct rtnl_link *link;
    235     uint32_t ht, htlink, htid, direction;
    236     char chashlink[16]="";
    237     int err;
    238     struct nl_cache *link_cache;
    239     struct rtnl_act *act;
    240 
    241     if (!(sock = nl_socket_alloc())) {
    242         printf("Unable to allocate netlink socket\n");
    243         exit(1);
    244     }
    245 
    246     if ((err = nl_connect(sock, NETLINK_ROUTE)) < 0 ) {
    247         printf("Nu s-a putut conecta la NETLINK!\n");
    248         nl_socket_free(sock);
    249         exit(1);
    250     }
    251 
    252     if ((err = rtnl_link_alloc_cache(sock, AF_UNSPEC, &link_cache)) < 0) {
    253         printf("Unable to allocate link cache: %s\n",
    254                              nl_geterror(err));
    255         nl_socket_free(sock);
    256         exit(1);
    257     }
    258 
    259     /* lookup interface index of eth0 */
    260     if (!(link = rtnl_link_get_by_name(link_cache, "eth0"))) {
    261         /* error */
    262         printf("Interface not found\n");
    263         nl_socket_free(sock);
    264         exit(1);
    265     }
    266 
    267     err=qdisc_add_ingress(sock, link);
    268     //printf("Add main hash table\n");
    269 
    270     /* create u32 first hash filter table
    271      *
    272      */
    273     /* formula calcul handle:
    274     *         uint32_t handle = (htid << 20) | (hash << 12) | nodeid;
    275     */
    276 
    277     /*
    278      * Upper limit of number of hash tables: 4096 (0xFFF)
    279      * Number of hashes in a table: 256 values (0xFF)
    280      *
    281      */
    282 
    283     /* using 256 values for hash table
    284      * each entry in hash table match a byte from IP address specified later by a hash key
    285      */
    286 
    287     uint32_t i;
    288     for (i = 1; i <= 0xf; i++)
    289 	u32_add_ht(sock, link, 1, i, 256);
    290 
    291     /*
    292      * attach a u32 filter to the first hash
    293      * that redirects all traffic and make a hash key
    294      * from the fist byte of the IP address
    295      *
    296      */
    297 
    298     //divisor=0x0;	// unused here
    299     //handle = 0x0;	// unused here
    300     //hash = 0x0;		// unused here
    301     //htid = 0x0;		// unused here
    302     //nodeid = 0x0;	// unused here
    303 
    304     // direction = 12 -> source IP
    305     // direction = 16 -> destination IP
    306     direction = 16;
    307 
    308     /*
    309      * which hash table will use
    310      * in our case is hash table no 1 defined previous
    311      *
    312      * There are 2 posibilities to set the the hash table:
    313      * 1. Using function get_u32_handle and sent a string in
    314      *  format 10: where 10 is number of the hash table
    315      * 2. Create your own value in format: 0xa00000
    316      *
    317      */
    318     strcpy(chashlink, "1:");
    319     //printf("Hash Link: %s\n", chashlink);
    320     //chashlink=malloc(sizeof(char) *
    321     htlink = 0x0;		// is used by get_u32_handle to return the correct value of hash table (link)
    322 
    323     if(get_u32_handle(&htlink, chashlink)) {
    324         printf ("Illegal \"link\"");
    325         nl_socket_free(sock);
    326         exit(1);
    327     }
    328     //printf ("hash link : 0x%X\n", htlink);
    329     //printf ("hash link test : %u\n", (htlink && TC_U32_NODE(htlink)));
    330 
    331     if (htlink && TC_U32_NODE(htlink)) {
    332 	printf("\"link\" must be a hash table.\n");
    333         nl_socket_free(sock);
    334         exit(1);
    335     }
    336 
    337     /* the hash mask will hit the hash table (link) no 1: in our case
    338      */
    339 
    340     /* set the hash key mask */
    341     //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
    342 
    343     /* Here we add a hash filter which match the first byte (see the hashmask value)
    344      * of the source IP (offset 12 in the packet header)
    345      * You can use also offset 16 to match the destination IP
    346      */
    347 
    348     /*
    349      * Also we need a filter to match our rule
    350      * This mean that we will put a 0.0.0.0/0 filter in our first rule
    351      * that match the offset 12 (source IP)
    352      * Also you can put offset 16 to match the destination IP
    353      */
    354 
    355     u32_add_filter_on_ht_with_hashmask(sock, link, 1,
    356 	    0x0, 0x0, direction, 0,
    357 	    0, htlink, 0xff000000, direction, NULL);
    358 
    359     /*
    360      * For each first byte that we need to match we will create a new hash table
    361      * For example: you have those clases: 10.0.0.0/24 and 172.16.0.0/23
    362      * For byte 10 and byte 172 will create a separate hash table that will match the second
    363      * byte from each class.
    364      *
    365      */
    366 
    367 
    368     /*
    369      * Now we will create other filter under (ATENTION) our first hash table (link) 1:
    370      * Previous rule redirects the trafic according the hash mask to hash table (link) no 1:
    371      * Here we will match the hash tables from 1:0 to 1:ff. Under each hash table we will attach
    372      * other rules that matches next byte from IP source/destination IP and we will repeat the
    373      * previous steps.
    374      *
    375      */
    376 
    377     act = rtnl_act_alloc();
    378     if (!act) {
    379             printf("rtnl_act_alloc() returns %p\n", act);
    380             return -1;
    381    }
    382     rtnl_tc_set_kind(TC_CAST(act), "mirred");
    383     rtnl_mirred_set_action(act, TCA_EGRESS_REDIR);
    384     rtnl_mirred_set_policy(act, TC_ACT_STOLEN);
    385     rtnl_mirred_set_ifindex(act, rtnl_link_name2i(link_cache, "eth1"));
    386     // /8 check
    387 
    388     // 10.0.0.0/8
    389     ht=get_u32_parse_handle("1:a:");
    390     htid = (ht&0xFFFFF000);
    391     htlink=get_u32_parse_handle("2:");
    392 
    393     u32_add_filter_on_ht_with_hashmask(sock, link, 1,
    394 	    0x0a000000, 0xff000000, direction, 0,
    395 	    htid, htlink, 0x00ff0000, direction, act);
    396 
    397     rtnl_act_put(act);
    398     nl_socket_free(sock);
    399     return 0;
    400 }
    401