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