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