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/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 */
get_u32(__u32 * val,const char * arg,int base)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 
get_u32_handle(__u32 * handle,const char * str)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 
get_u32_parse_handle(const char * cHandle)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
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)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
u32_add_ht(struct nl_sock * sock,struct rtnl_link * rtnlLink,uint32_t prio,uint32_t htid,uint32_t divisor)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
qdisc_add_ingress(struct nl_sock * sock,struct rtnl_link * rtnlLink)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 
main(void)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