• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * q_u32.c		U32 filter.
3  *
4  *		This program is free software; you can u32istribute it and/or
5  *		modify it under the terms of the GNU General Public License
6  *		as published by the Free Software Foundation; either version
7  *		2 of the License, or (at your option) any later version.
8  *
9  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
10  *		Match mark added by Catalin(ux aka Dino) BOIE <catab at umbrella.ro> [5 nov 2004]
11  *
12  */
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <syslog.h>
18 #include <fcntl.h>
19 #include <sys/socket.h>
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <string.h>
23 #include <linux/if.h>
24 #include <linux/if_ether.h>
25 
26 #include "utils.h"
27 #include "tc_util.h"
28 
29 extern int show_pretty;
30 
explain(void)31 static void explain(void)
32 {
33 	fprintf(stderr, "Usage: ... u32 [ match SELECTOR ... ] [ link HTID ]"
34 		" [ classid CLASSID ]\n");
35 	fprintf(stderr, "               [ police POLICE_SPEC ]"
36 		" [ offset OFFSET_SPEC ]\n");
37 	fprintf(stderr, "               [ ht HTID ] [ hashkey HASHKEY_SPEC ]\n");
38 	fprintf(stderr, "               [ sample SAMPLE ]\n");
39 	fprintf(stderr, "or         u32 divisor DIVISOR\n");
40 	fprintf(stderr, "\n");
41 	fprintf(stderr, "Where: SELECTOR := SAMPLE SAMPLE ...\n");
42 	fprintf(stderr, "       SAMPLE := { ip | ip6 | udp | tcp | icmp |"
43 		" u{32|16|8} | mark } SAMPLE_ARGS [divisor DIVISOR]\n");
44 	fprintf(stderr, "       FILTERID := X:Y:Z\n");
45 	fprintf(stderr, "\nNOTE: CLASSID is parsed at hexadecimal input.\n");
46 }
47 
get_u32_handle(__u32 * handle,const char * str)48 int get_u32_handle(__u32 *handle, const char *str)
49 {
50 	__u32 htid=0, hash=0, nodeid=0;
51 	char *tmp = strchr(str, ':');
52 
53 	if (tmp == NULL) {
54 		if (memcmp("0x", str, 2) == 0)
55 			return get_u32(handle, str, 16);
56 		return -1;
57 	}
58 	htid = strtoul(str, &tmp, 16);
59 	if (tmp == str && *str != ':' && *str != 0)
60 		return -1;
61 	if (htid>=0x1000)
62 		return -1;
63 	if (*tmp) {
64 		str = tmp+1;
65 		hash = strtoul(str, &tmp, 16);
66 		if (tmp == str && *str != ':' && *str != 0)
67 			return -1;
68 		if (hash>=0x100)
69 			return -1;
70 		if (*tmp) {
71 			str = tmp+1;
72 			nodeid = strtoul(str, &tmp, 16);
73 			if (tmp == str && *str != 0)
74 				return -1;
75 			if (nodeid>=0x1000)
76 				return -1;
77 		}
78 	}
79 	*handle = (htid<<20)|(hash<<12)|nodeid;
80 	return 0;
81 }
82 
sprint_u32_handle(__u32 handle,char * buf)83 char * sprint_u32_handle(__u32 handle, char *buf)
84 {
85 	int bsize = SPRINT_BSIZE-1;
86 	__u32 htid = TC_U32_HTID(handle);
87 	__u32 hash = TC_U32_HASH(handle);
88 	__u32 nodeid = TC_U32_NODE(handle);
89 	char *b = buf;
90 
91 	if (handle == 0) {
92 		snprintf(b, bsize, "none");
93 		return b;
94 	}
95 	if (htid) {
96 		int l = snprintf(b, bsize, "%x:", htid>>20);
97 		bsize -= l;
98 		b += l;
99 	}
100 	if (nodeid|hash) {
101 		if (hash) {
102 			int l = snprintf(b, bsize, "%x", hash);
103 			bsize -= l;
104 			b += l;
105 		}
106 		if (nodeid) {
107 			int l = snprintf(b, bsize, ":%x", nodeid);
108 			bsize -= l;
109 			b += l;
110 		}
111 	}
112 	if (show_raw)
113 		snprintf(b, bsize, "[%08x] ", handle);
114 	return buf;
115 }
116 
pack_key(struct tc_u32_sel * sel,__u32 key,__u32 mask,int off,int offmask)117 static int pack_key(struct tc_u32_sel *sel, __u32 key, __u32 mask,
118 		    int off, int offmask)
119 {
120 	int i;
121 	int hwm = sel->nkeys;
122 
123 	key &= mask;
124 
125 	for (i=0; i<hwm; i++) {
126 		if (sel->keys[i].off == off && sel->keys[i].offmask == offmask) {
127 			__u32 intersect = mask&sel->keys[i].mask;
128 
129 			if ((key^sel->keys[i].val) & intersect)
130 				return -1;
131 			sel->keys[i].val |= key;
132 			sel->keys[i].mask |= mask;
133 			return 0;
134 		}
135 	}
136 
137 	if (hwm >= 128)
138 		return -1;
139 	if (off % 4)
140 		return -1;
141 	sel->keys[hwm].val = key;
142 	sel->keys[hwm].mask = mask;
143 	sel->keys[hwm].off = off;
144 	sel->keys[hwm].offmask = offmask;
145 	sel->nkeys++;
146 	return 0;
147 }
148 
pack_key32(struct tc_u32_sel * sel,__u32 key,__u32 mask,int off,int offmask)149 static int pack_key32(struct tc_u32_sel *sel, __u32 key, __u32 mask,
150 		      int off, int offmask)
151 {
152 	key = htonl(key);
153 	mask = htonl(mask);
154 	return pack_key(sel, key, mask, off, offmask);
155 }
156 
pack_key16(struct tc_u32_sel * sel,__u32 key,__u32 mask,int off,int offmask)157 static int pack_key16(struct tc_u32_sel *sel, __u32 key, __u32 mask,
158 		      int off, int offmask)
159 {
160 	if (key > 0xFFFF || mask > 0xFFFF)
161 		return -1;
162 
163 	if ((off & 3) == 0) {
164 		key <<= 16;
165 		mask <<= 16;
166 	}
167 	off &= ~3;
168 	key = htonl(key);
169 	mask = htonl(mask);
170 
171 	return pack_key(sel, key, mask, off, offmask);
172 }
173 
pack_key8(struct tc_u32_sel * sel,__u32 key,__u32 mask,int off,int offmask)174 static int pack_key8(struct tc_u32_sel *sel, __u32 key, __u32 mask, int off, int offmask)
175 {
176 	if (key > 0xFF || mask > 0xFF)
177 		return -1;
178 
179 	if ((off & 3) == 0) {
180 		key <<= 24;
181 		mask <<= 24;
182 	} else if ((off & 3) == 1) {
183 		key <<= 16;
184 		mask <<= 16;
185 	} else if ((off & 3) == 2) {
186 		key <<= 8;
187 		mask <<= 8;
188 	}
189 	off &= ~3;
190 	key = htonl(key);
191 	mask = htonl(mask);
192 
193 	return pack_key(sel, key, mask, off, offmask);
194 }
195 
196 
parse_at(int * argc_p,char *** argv_p,int * off,int * offmask)197 int parse_at(int *argc_p, char ***argv_p, int *off, int *offmask)
198 {
199 	int argc = *argc_p;
200 	char **argv = *argv_p;
201 	char *p = *argv;
202 
203 	if (argc <= 0)
204 		return -1;
205 
206 	if (strlen(p) > strlen("nexthdr+") &&
207 	    memcmp(p, "nexthdr+", strlen("nexthdr+")) == 0) {
208 		*offmask = -1;
209 		p += strlen("nexthdr+");
210 	} else if (matches(*argv, "nexthdr+") == 0) {
211 		NEXT_ARG();
212 		*offmask = -1;
213 		p = *argv;
214 	}
215 
216 	if (get_integer(off, p, 0))
217 		return -1;
218 	argc--; argv++;
219 
220 	*argc_p = argc;
221 	*argv_p = argv;
222 	return 0;
223 }
224 
225 
parse_u32(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off,int offmask)226 static int parse_u32(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
227 		     int off, int offmask)
228 {
229 	int res = -1;
230 	int argc = *argc_p;
231 	char **argv = *argv_p;
232 	__u32 key;
233 	__u32 mask;
234 
235 	if (argc < 2)
236 		return -1;
237 
238 	if (get_u32(&key, *argv, 0))
239 		return -1;
240 	argc--; argv++;
241 
242 	if (get_u32(&mask, *argv, 16))
243 		return -1;
244 	argc--; argv++;
245 
246 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
247 		NEXT_ARG();
248 		if (parse_at(&argc, &argv, &off, &offmask))
249 			return -1;
250 	}
251 
252 	res = pack_key32(sel, key, mask, off, offmask);
253 	*argc_p = argc;
254 	*argv_p = argv;
255 	return res;
256 }
257 
parse_u16(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off,int offmask)258 static int parse_u16(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
259 		     int off, int offmask)
260 {
261 	int res = -1;
262 	int argc = *argc_p;
263 	char **argv = *argv_p;
264 	__u32 key;
265 	__u32 mask;
266 
267 	if (argc < 2)
268 		return -1;
269 
270 	if (get_u32(&key, *argv, 0))
271 		return -1;
272 	argc--; argv++;
273 
274 	if (get_u32(&mask, *argv, 16))
275 		return -1;
276 	argc--; argv++;
277 
278 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
279 		NEXT_ARG();
280 		if (parse_at(&argc, &argv, &off, &offmask))
281 			return -1;
282 	}
283 	res = pack_key16(sel, key, mask, off, offmask);
284 	*argc_p = argc;
285 	*argv_p = argv;
286 	return res;
287 }
288 
parse_u8(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off,int offmask)289 static int parse_u8(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
290 		    int off, int offmask)
291 {
292 	int res = -1;
293 	int argc = *argc_p;
294 	char **argv = *argv_p;
295 	__u32 key;
296 	__u32 mask;
297 
298 	if (argc < 2)
299 		return -1;
300 
301 	if (get_u32(&key, *argv, 0))
302 		return -1;
303 	argc--; argv++;
304 
305 	if (get_u32(&mask, *argv, 16))
306 		return -1;
307 	argc--; argv++;
308 
309 	if (key > 0xFF || mask > 0xFF)
310 		return -1;
311 
312 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
313 		NEXT_ARG();
314 		if (parse_at(&argc, &argv, &off, &offmask))
315 			return -1;
316 	}
317 
318 	res = pack_key8(sel, key, mask, off, offmask);
319 	*argc_p = argc;
320 	*argv_p = argv;
321 	return res;
322 }
323 
parse_ip_addr(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off)324 static int parse_ip_addr(int *argc_p, char ***argv_p, struct tc_u32_sel *sel,
325 			 int off)
326 {
327 	int res = -1;
328 	int argc = *argc_p;
329 	char **argv = *argv_p;
330 	inet_prefix addr;
331 	__u32 mask;
332 	int offmask = 0;
333 
334 	if (argc < 1)
335 		return -1;
336 
337 	if (get_prefix_1(&addr, *argv, AF_INET))
338 		return -1;
339 	argc--; argv++;
340 
341 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
342 		NEXT_ARG();
343 		if (parse_at(&argc, &argv, &off, &offmask))
344 			return -1;
345 	}
346 
347 	mask = 0;
348 	if (addr.bitlen)
349 		mask = htonl(0xFFFFFFFF<<(32-addr.bitlen));
350 	if (pack_key(sel, addr.data[0], mask, off, offmask) < 0)
351 		return -1;
352 	res = 0;
353 
354 	*argc_p = argc;
355 	*argv_p = argv;
356 	return res;
357 }
358 
parse_ip6_addr(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off)359 static int parse_ip6_addr(int *argc_p, char ***argv_p,
360 			  struct tc_u32_sel *sel, int off)
361 {
362 	int res = -1;
363 	int argc = *argc_p;
364 	char **argv = *argv_p;
365 	int plen = 128;
366 	int i;
367 	inet_prefix addr;
368 	int offmask = 0;
369 
370 	if (argc < 1)
371 		return -1;
372 
373 	if (get_prefix_1(&addr, *argv, AF_INET6))
374 		return -1;
375 	argc--; argv++;
376 
377 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
378 		NEXT_ARG();
379 		if (parse_at(&argc, &argv, &off, &offmask))
380 			return -1;
381 	}
382 
383 	plen = addr.bitlen;
384 	for (i=0; i<plen; i+=32) {
385 //		if (((i+31)&~0x1F)<=plen) {
386 		if (i + 31 <= plen) {
387 			res = pack_key(sel, addr.data[i/32],
388 				       0xFFFFFFFF, off+4*(i/32), offmask);
389 			if (res < 0)
390 				return -1;
391 		} else if (i < plen) {
392 			__u32 mask = htonl(0xFFFFFFFF << (32 - (plen -i )));
393 			res = pack_key(sel, addr.data[i/32],
394 				       mask, off+4*(i/32), offmask);
395 			if (res < 0)
396 				return -1;
397 		}
398 	}
399 	res = 0;
400 
401 	*argc_p = argc;
402 	*argv_p = argv;
403 	return res;
404 }
405 
parse_ip6_class(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)406 static int parse_ip6_class(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
407 {
408 	int res = -1;
409 	int argc = *argc_p;
410 	char **argv = *argv_p;
411 	__u32 key;
412 	__u32 mask;
413 	int off = 0;
414 	int offmask = 0;
415 
416 	if (argc < 2)
417 		return -1;
418 
419 	if (get_u32(&key, *argv, 0))
420 		return -1;
421 	argc--; argv++;
422 
423 	if (get_u32(&mask, *argv, 16))
424 		return -1;
425 	argc--; argv++;
426 
427 	if (key > 0xFF || mask > 0xFF)
428 		return -1;
429 
430 	key <<= 20;
431 	mask <<= 20;
432 	key = htonl(key);
433 	mask = htonl(mask);
434 
435 	if (res = pack_key(sel, key, mask, off, offmask) < 0)
436 		return -1;
437 
438 	*argc_p = argc;
439 	*argv_p = argv;
440 	return 0;
441 }
442 
parse_ether_addr(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,int off)443 static int parse_ether_addr(int *argc_p, char ***argv_p,
444 			    struct tc_u32_sel *sel, int off)
445 {
446 	int res = -1;
447 	int argc = *argc_p;
448 	char **argv = *argv_p;
449 	__u8 addr[6];
450 	int offmask = 0;
451 	int i;
452 
453 	if (argc < 1)
454 		return -1;
455 
456 	if (sscanf(*argv, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
457 		   addr + 0, addr + 1, addr + 2,
458 		   addr + 3, addr + 4, addr + 5) != 6) {
459 		fprintf(stderr, "parse_ether_addr: improperly formed address '%s'\n",
460 			*argv);
461 		return -1;
462 	}
463 
464 	argc--; argv++;
465 	if (argc > 0 && strcmp(argv[0], "at") == 0) {
466 		NEXT_ARG();
467 		if (parse_at(&argc, &argv, &off, &offmask))
468 			return -1;
469 	}
470 
471 	for (i = 0; i < 6; i++) {
472 		res = pack_key8(sel, addr[i], 0xFF, off + i, offmask);
473 		if (res < 0)
474 			return -1;
475 	}
476 
477 	*argc_p = argc;
478 	*argv_p = argv;
479 	return res;
480 }
481 
parse_ip(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)482 static int parse_ip(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
483 {
484 	int res = -1;
485 	int argc = *argc_p;
486 	char **argv = *argv_p;
487 
488 	if (argc < 2)
489 		return -1;
490 
491 	if (strcmp(*argv, "src") == 0) {
492 		NEXT_ARG();
493 		res = parse_ip_addr(&argc, &argv, sel, 12);
494 	} else if (strcmp(*argv, "dst") == 0) {
495 		NEXT_ARG();
496 		res = parse_ip_addr(&argc, &argv, sel, 16);
497 	} else if (strcmp(*argv, "tos") == 0 ||
498 	    matches(*argv, "dsfield") == 0) {
499 		NEXT_ARG();
500 		res = parse_u8(&argc, &argv, sel, 1, 0);
501 	} else if (strcmp(*argv, "ihl") == 0) {
502 		NEXT_ARG();
503 		res = parse_u8(&argc, &argv, sel, 0, 0);
504 	} else if (strcmp(*argv, "protocol") == 0) {
505 		NEXT_ARG();
506 		res = parse_u8(&argc, &argv, sel, 9, 0);
507 	} else if (matches(*argv, "precedence") == 0) {
508 		NEXT_ARG();
509 		res = parse_u8(&argc, &argv, sel, 1, 0);
510 	} else if (strcmp(*argv, "nofrag") == 0) {
511 		argc--; argv++;
512 		res = pack_key16(sel, 0, 0x3FFF, 6, 0);
513 	} else if (strcmp(*argv, "firstfrag") == 0) {
514 		argc--; argv++;
515 		res = pack_key16(sel, 0, 0x1FFF, 6, 0);
516 	} else if (strcmp(*argv, "df") == 0) {
517 		argc--; argv++;
518 		res = pack_key16(sel, 0x4000, 0x4000, 6, 0);
519 	} else if (strcmp(*argv, "mf") == 0) {
520 		argc--; argv++;
521 		res = pack_key16(sel, 0x2000, 0x2000, 6, 0);
522 	} else if (strcmp(*argv, "dport") == 0) {
523 		NEXT_ARG();
524 		res = parse_u16(&argc, &argv, sel, 22, 0);
525 	} else if (strcmp(*argv, "sport") == 0) {
526 		NEXT_ARG();
527 		res = parse_u16(&argc, &argv, sel, 20, 0);
528 	} else if (strcmp(*argv, "icmp_type") == 0) {
529 		NEXT_ARG();
530 		res = parse_u8(&argc, &argv, sel, 20, 0);
531 	} else if (strcmp(*argv, "icmp_code") == 0) {
532 		NEXT_ARG();
533 		res = parse_u8(&argc, &argv, sel, 20, 1);
534 	} else
535 		return -1;
536 
537 	*argc_p = argc;
538 	*argv_p = argv;
539 	return res;
540 }
541 
parse_ip6(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)542 static int parse_ip6(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
543 {
544 	int res = -1;
545 	int argc = *argc_p;
546 	char **argv = *argv_p;
547 
548 	if (argc < 2)
549 		return -1;
550 
551 	if (strcmp(*argv, "src") == 0) {
552 		NEXT_ARG();
553 		res = parse_ip6_addr(&argc, &argv, sel, 8);
554 	} else if (strcmp(*argv, "dst") == 0) {
555 		NEXT_ARG();
556 		res = parse_ip6_addr(&argc, &argv, sel, 24);
557 	} else if (strcmp(*argv, "priority") == 0) {
558 		NEXT_ARG();
559 		res = parse_ip6_class(&argc, &argv, sel);
560 	} else if (strcmp(*argv, "protocol") == 0) {
561 		NEXT_ARG();
562 		res = parse_u8(&argc, &argv, sel, 6, 0);
563 	} else if (strcmp(*argv, "flowlabel") == 0) {
564 		NEXT_ARG();
565 		res = parse_u32(&argc, &argv, sel, 0, 0);
566 	} else if (strcmp(*argv, "dport") == 0) {
567 		NEXT_ARG();
568 		res = parse_u16(&argc, &argv, sel, 42, 0);
569 	} else if (strcmp(*argv, "sport") == 0) {
570 		NEXT_ARG();
571 		res = parse_u16(&argc, &argv, sel, 40, 0);
572 	} else if (strcmp(*argv, "icmp_type") == 0) {
573 		NEXT_ARG();
574 		res = parse_u8(&argc, &argv, sel, 40, 0);
575 	} else if (strcmp(*argv, "icmp_code") == 0) {
576 		NEXT_ARG();
577 		res = parse_u8(&argc, &argv, sel, 41, 1);
578 	} else
579 		return -1;
580 
581 	*argc_p = argc;
582 	*argv_p = argv;
583 	return res;
584 }
585 
parse_ether(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)586 static int parse_ether(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
587 {
588 	int res = -1;
589 	int argc = *argc_p;
590 	char **argv = *argv_p;
591 
592 	if (argc < 2)
593 		return -1;
594 
595 	if (strcmp(*argv, "src") == 0) {
596 		NEXT_ARG();
597 		res = parse_ether_addr(&argc, &argv, sel, -8);
598 	} else if (strcmp(*argv, "dst") == 0) {
599 		NEXT_ARG();
600 		res = parse_ether_addr(&argc, &argv, sel, -14);
601 	} else {
602 		fprintf(stderr, "Unknown match: ether %s\n", *argv);
603 		return -1;
604 	}
605 
606 	*argc_p = argc;
607 	*argv_p = argv;
608 	return res;
609 }
610 
611 #define parse_tcp parse_udp
parse_udp(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)612 static int parse_udp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
613 {
614 	int res = -1;
615 	int argc = *argc_p;
616 	char **argv = *argv_p;
617 
618 	if (argc < 2)
619 		return -1;
620 
621 	if (strcmp(*argv, "src") == 0) {
622 		NEXT_ARG();
623 		res = parse_u16(&argc, &argv, sel, 0, -1);
624 	} else if (strcmp(*argv, "dst") == 0) {
625 		NEXT_ARG();
626 		res = parse_u16(&argc, &argv, sel, 2, -1);
627 	} else
628 		return -1;
629 
630 	*argc_p = argc;
631 	*argv_p = argv;
632 	return res;
633 }
634 
635 
parse_icmp(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)636 static int parse_icmp(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
637 {
638 	int res = -1;
639 	int argc = *argc_p;
640 	char **argv = *argv_p;
641 
642 	if (argc < 2)
643 		return -1;
644 
645 	if (strcmp(*argv, "type") == 0) {
646 		NEXT_ARG();
647 		res = parse_u8(&argc, &argv, sel, 0, -1);
648 	} else if (strcmp(*argv, "code") == 0) {
649 		NEXT_ARG();
650 		res = parse_u8(&argc, &argv, sel, 1, -1);
651 	} else
652 		return -1;
653 
654 	*argc_p = argc;
655 	*argv_p = argv;
656 	return res;
657 }
658 
parse_mark(int * argc_p,char *** argv_p,struct nlmsghdr * n)659 static int parse_mark(int *argc_p, char ***argv_p, struct nlmsghdr *n)
660 {
661 	int res = -1;
662 	int argc = *argc_p;
663 	char **argv = *argv_p;
664 	struct tc_u32_mark mark;
665 
666 	if (argc <= 1)
667 		return -1;
668 
669 	if (get_u32(&mark.val, *argv, 0)) {
670 		fprintf(stderr, "Illegal \"mark\" value\n");
671 		return -1;
672 	}
673 	NEXT_ARG();
674 
675 	if (get_u32(&mark.mask, *argv, 0)) {
676 		fprintf(stderr, "Illegal \"mark\" mask\n");
677 		return -1;
678 	}
679 	NEXT_ARG();
680 
681 	if ((mark.val & mark.mask) != mark.val) {
682 		fprintf(stderr, "Illegal \"mark\" (impossible combination)\n");
683 		return -1;
684 	}
685 
686 	addattr_l(n, MAX_MSG, TCA_U32_MARK, &mark, sizeof(mark));
687 	res = 0;
688 
689 	*argc_p = argc;
690 	*argv_p = argv;
691 	return res;
692 }
693 
parse_selector(int * argc_p,char *** argv_p,struct tc_u32_sel * sel,struct nlmsghdr * n)694 static int parse_selector(int *argc_p, char ***argv_p,
695 			  struct tc_u32_sel *sel, struct nlmsghdr *n)
696 {
697 	int argc = *argc_p;
698 	char **argv = *argv_p;
699 	int res = -1;
700 
701 	if (argc <= 0)
702 		return -1;
703 
704 	if (matches(*argv, "u32") == 0) {
705 		NEXT_ARG();
706 		res = parse_u32(&argc, &argv, sel, 0, 0);
707 	} else if (matches(*argv, "u16") == 0) {
708 		NEXT_ARG();
709 		res = parse_u16(&argc, &argv, sel, 0, 0);
710 	} else if (matches(*argv, "u8") == 0) {
711 		NEXT_ARG();
712 		res = parse_u8(&argc, &argv, sel, 0, 0);
713 	} else if (matches(*argv, "ip") == 0) {
714 		NEXT_ARG();
715 		res = parse_ip(&argc, &argv, sel);
716 	} else 	if (matches(*argv, "ip6") == 0) {
717 		NEXT_ARG();
718 		res = parse_ip6(&argc, &argv, sel);
719 	} else if (matches(*argv, "udp") == 0) {
720 		NEXT_ARG();
721 		res = parse_udp(&argc, &argv, sel);
722 	} else if (matches(*argv, "tcp") == 0) {
723 		NEXT_ARG();
724 		res = parse_tcp(&argc, &argv, sel);
725 	} else if (matches(*argv, "icmp") == 0) {
726 		NEXT_ARG();
727 		res = parse_icmp(&argc, &argv, sel);
728 	} else if (matches(*argv, "mark") == 0) {
729 		NEXT_ARG();
730 		res = parse_mark(&argc, &argv, n);
731 	} else if (matches(*argv, "ether") == 0) {
732 		NEXT_ARG();
733 		res = parse_ether(&argc, &argv, sel);
734 	} else
735 		return -1;
736 
737 	*argc_p = argc;
738 	*argv_p = argv;
739 	return res;
740 }
741 
parse_offset(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)742 static int parse_offset(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
743 {
744 	int argc = *argc_p;
745 	char **argv = *argv_p;
746 
747 	while (argc > 0) {
748 		if (matches(*argv, "plus") == 0) {
749 			int off;
750 			NEXT_ARG();
751 			if (get_integer(&off, *argv, 0))
752 				return -1;
753 			sel->off = off;
754 			sel->flags |= TC_U32_OFFSET;
755 		} else if (matches(*argv, "at") == 0) {
756 			int off;
757 			NEXT_ARG();
758 			if (get_integer(&off, *argv, 0))
759 				return -1;
760 			sel->offoff = off;
761 			if (off%2) {
762 				fprintf(stderr, "offset \"at\" must be even\n");
763 				return -1;
764 			}
765 			sel->flags |= TC_U32_VAROFFSET;
766 		} else if (matches(*argv, "mask") == 0) {
767 			__u16 mask;
768 			NEXT_ARG();
769 			if (get_u16(&mask, *argv, 16))
770 				return -1;
771 			sel->offmask = htons(mask);
772 			sel->flags |= TC_U32_VAROFFSET;
773 		} else if (matches(*argv, "shift") == 0) {
774 			int shift;
775 			NEXT_ARG();
776 			if (get_integer(&shift, *argv, 0))
777 				return -1;
778 			sel->offshift = shift;
779 			sel->flags |= TC_U32_VAROFFSET;
780 		} else if (matches(*argv, "eat") == 0) {
781 			sel->flags |= TC_U32_EAT;
782 		} else {
783 			break;
784 		}
785 		argc--; argv++;
786 	}
787 
788 	*argc_p = argc;
789 	*argv_p = argv;
790 	return 0;
791 }
792 
parse_hashkey(int * argc_p,char *** argv_p,struct tc_u32_sel * sel)793 static int parse_hashkey(int *argc_p, char ***argv_p, struct tc_u32_sel *sel)
794 {
795 	int argc = *argc_p;
796 	char **argv = *argv_p;
797 
798 	while (argc > 0) {
799 		if (matches(*argv, "mask") == 0) {
800 			__u32 mask;
801 			NEXT_ARG();
802 			if (get_u32(&mask, *argv, 16))
803 				return -1;
804 			sel->hmask = htonl(mask);
805 		} else if (matches(*argv, "at") == 0) {
806 			int num;
807 			NEXT_ARG();
808 			if (get_integer(&num, *argv, 0))
809 				return -1;
810 			if (num%4)
811 				return -1;
812 			sel->hoff = num;
813 		} else {
814 			break;
815 		}
816 		argc--; argv++;
817 	}
818 
819 	*argc_p = argc;
820 	*argv_p = argv;
821 	return 0;
822 }
823 
print_ipv4(FILE * f,const struct tc_u32_key * key)824 static void print_ipv4(FILE *f, const struct tc_u32_key *key)
825 {
826 	char abuf[256];
827 
828 	switch (key->off) {
829 	case 0:
830 		switch (ntohl(key->mask)) {
831 		case 0x0f000000:
832 			fprintf(f, "\n  match IP ihl %u", ntohl(key->val) >> 24);
833 			return;
834 		case 0x00ff0000:
835 			fprintf(f, "\n  match IP dsfield %#x", ntohl(key->val) >> 16);
836 			return;
837 		}
838 		break;
839 	case 8:
840 		if (ntohl(key->mask) == 0x00ff0000) {
841 			fprintf(f, "\n  match IP protocol %d", ntohl(key->val) >> 16);
842 			return;
843 		}
844 		break;
845 	case 12:
846 	case 16: {
847 			int bits = mask2bits(key->mask);
848 			if (bits >= 0) {
849 				fprintf(f, "\n  %s %s/%d",
850 					key->off == 12 ? "match IP src" : "match IP dst",
851 					inet_ntop(AF_INET, &key->val,
852 						  abuf, sizeof(abuf)),
853 					bits);
854 				return;
855 			}
856 		}
857 		break;
858 
859 	case 20:
860 		switch (ntohl(key->mask)) {
861 		case 0x0000ffff:
862 			fprintf(f, "\n  match sport %u",
863 				ntohl(key->val) & 0xffff);
864 			return;
865 		case 0xffff0000:
866 			fprintf(f, "\n  match dport %u",
867 				ntohl(key->val) >> 16);
868 			return;
869 		case 0xffffffff:
870 			fprintf(f, "\n  match sport %u, match dport %u",
871 				ntohl(key->val) & 0xffff,
872 				ntohl(key->val) >> 16);
873 
874 			return;
875 		}
876 		/* XXX: Default print_raw */
877 	}
878 }
879 
print_ipv6(FILE * f,const struct tc_u32_key * key)880 static void print_ipv6(FILE *f, const struct tc_u32_key *key)
881 {
882 	char abuf[256];
883 
884 	switch (key->off) {
885 	case 0:
886 		switch (ntohl(key->mask)) {
887 		case 0x0f000000:
888 			fprintf(f, "\n  match IP ihl %u", ntohl(key->val) >> 24);
889 			return;
890 		case 0x00ff0000:
891 			fprintf(f, "\n  match IP dsfield %#x", ntohl(key->val) >> 16);
892 			return;
893 		}
894 		break;
895 	case 8:
896 		if (ntohl(key->mask) == 0x00ff0000) {
897 			fprintf(f, "\n  match IP protocol %d", ntohl(key->val) >> 16);
898 			return;
899 		}
900 		break;
901 	case 12:
902 	case 16: {
903 			int bits = mask2bits(key->mask);
904 			if (bits >= 0) {
905 				fprintf(f, "\n  %s %s/%d",
906 					key->off == 12 ? "match IP src" : "match IP dst",
907 					inet_ntop(AF_INET, &key->val,
908 						  abuf, sizeof(abuf)),
909 					bits);
910 				return;
911 			}
912 		}
913 		break;
914 
915 	case 20:
916 		switch (ntohl(key->mask)) {
917 		case 0x0000ffff:
918 			fprintf(f, "\n  match sport %u",
919 				ntohl(key->val) & 0xffff);
920 			return;
921 		case 0xffff0000:
922 			fprintf(f, "\n  match dport %u",
923 				ntohl(key->val) >> 16);
924 			return;
925 		case 0xffffffff:
926 			fprintf(f, "\n  match sport %u, match dport %u",
927 				ntohl(key->val) & 0xffff,
928 				ntohl(key->val) >> 16);
929 
930 			return;
931 		}
932 		/* XXX: Default print_raw */
933 	}
934 }
935 
print_raw(FILE * f,const struct tc_u32_key * key)936 static void print_raw(FILE *f, const struct tc_u32_key *key)
937 {
938 	fprintf(f, "\n  match %08x/%08x at %s%d",
939 		(unsigned int)ntohl(key->val),
940 		(unsigned int)ntohl(key->mask),
941 		key->offmask ? "nexthdr+" : "",
942 		key->off);
943 }
944 
945 static const struct {
946 	__u16 proto;
947 	__u16 pad;
948 	void (*pprinter)(FILE *f, const struct tc_u32_key *key);
949 } u32_pprinters[] = {
950 	{0, 	   0, print_raw},
951 	{ETH_P_IP, 0, print_ipv4},
952 	{ETH_P_IPV6, 0, print_ipv6},
953 };
954 
show_keys(FILE * f,const struct tc_u32_key * key)955 static void show_keys(FILE *f, const struct tc_u32_key *key)
956 {
957 	int i = 0;
958 
959 	if (!show_pretty)
960 		goto show_k;
961 
962 	for (i = 0; i < sizeof(u32_pprinters) / sizeof(u32_pprinters[0]); i++) {
963 		if (u32_pprinters[i].proto == ntohs(f_proto)) {
964 show_k:
965 			u32_pprinters[i].pprinter(f, key);
966 			return;
967 		}
968 	}
969 
970 	i = 0;
971 	goto show_k;
972 }
973 
u32_parse_opt(struct filter_util * qu,char * handle,int argc,char ** argv,struct nlmsghdr * n)974 static int u32_parse_opt(struct filter_util *qu, char *handle,
975 			 int argc, char **argv, struct nlmsghdr *n)
976 {
977 	struct {
978 		struct tc_u32_sel sel;
979 		struct tc_u32_key keys[128];
980 	} sel;
981 	struct tcmsg *t = NLMSG_DATA(n);
982 	struct rtattr *tail;
983 	int sel_ok = 0, terminal_ok = 0;
984 	int sample_ok = 0;
985 	__u32 htid = 0;
986 	__u32 order = 0;
987 
988 	memset(&sel, 0, sizeof(sel));
989 
990 	if (handle && get_u32_handle(&t->tcm_handle, handle)) {
991 		fprintf(stderr, "Illegal filter ID\n");
992 		return -1;
993 	}
994 
995 	if (argc == 0)
996 		return 0;
997 
998 	tail = NLMSG_TAIL(n);
999 	addattr_l(n, MAX_MSG, TCA_OPTIONS, NULL, 0);
1000 
1001 	while (argc > 0) {
1002 		if (matches(*argv, "match") == 0) {
1003 			NEXT_ARG();
1004 			if (parse_selector(&argc, &argv, &sel.sel, n)) {
1005 				fprintf(stderr, "Illegal \"match\"\n");
1006 				return -1;
1007 			}
1008 			sel_ok++;
1009 			continue;
1010 		} else if (matches(*argv, "offset") == 0) {
1011 			NEXT_ARG();
1012 			if (parse_offset(&argc, &argv, &sel.sel)) {
1013 				fprintf(stderr, "Illegal \"offset\"\n");
1014 				return -1;
1015 			}
1016 			continue;
1017 		} else if (matches(*argv, "hashkey") == 0) {
1018 			NEXT_ARG();
1019 			if (parse_hashkey(&argc, &argv, &sel.sel)) {
1020 				fprintf(stderr, "Illegal \"hashkey\"\n");
1021 				return -1;
1022 			}
1023 			continue;
1024 		} else if (matches(*argv, "classid") == 0 ||
1025 			   strcmp(*argv, "flowid") == 0) {
1026 			unsigned handle;
1027 			NEXT_ARG();
1028 			if (get_tc_classid(&handle, *argv)) {
1029 				fprintf(stderr, "Illegal \"classid\"\n");
1030 				return -1;
1031 			}
1032 			addattr_l(n, MAX_MSG, TCA_U32_CLASSID, &handle, 4);
1033 			sel.sel.flags |= TC_U32_TERMINAL;
1034 		} else if (matches(*argv, "divisor") == 0) {
1035 			unsigned divisor;
1036 			NEXT_ARG();
1037 			if (get_unsigned(&divisor, *argv, 0) ||
1038 			    divisor == 0 ||
1039 			    divisor > 0x100 || ((divisor - 1) & divisor)) {
1040 				fprintf(stderr, "Illegal \"divisor\"\n");
1041 				return -1;
1042 			}
1043 			addattr_l(n, MAX_MSG, TCA_U32_DIVISOR, &divisor, 4);
1044 		} else if (matches(*argv, "order") == 0) {
1045 			NEXT_ARG();
1046 			if (get_u32(&order, *argv, 0)) {
1047 				fprintf(stderr, "Illegal \"order\"\n");
1048 				return -1;
1049 			}
1050 		} else if (strcmp(*argv, "link") == 0) {
1051 			unsigned handle;
1052 			NEXT_ARG();
1053 			if (get_u32_handle(&handle, *argv)) {
1054 				fprintf(stderr, "Illegal \"link\"\n");
1055 				return -1;
1056 			}
1057 			if (handle && TC_U32_NODE(handle)) {
1058 				fprintf(stderr, "\"link\" must be a hash table.\n");
1059 				return -1;
1060 			}
1061 			addattr_l(n, MAX_MSG, TCA_U32_LINK, &handle, 4);
1062 		} else if (strcmp(*argv, "ht") == 0) {
1063 			unsigned handle;
1064 			NEXT_ARG();
1065 			if (get_u32_handle(&handle, *argv)) {
1066 				fprintf(stderr, "Illegal \"ht\"\n");
1067 				return -1;
1068 			}
1069 			if (handle && TC_U32_NODE(handle)) {
1070 				fprintf(stderr, "\"ht\" must be a hash table.\n");
1071 				return -1;
1072 			}
1073 			if (sample_ok)
1074 				htid = (htid&0xFF000)|(handle&0xFFF00000);
1075 			else
1076 				htid = (handle&0xFFFFF000);
1077 		} else if (strcmp(*argv, "sample") == 0) {
1078 			__u32 hash;
1079 			unsigned divisor = 0x100;
1080 
1081 			struct {
1082 				struct tc_u32_sel sel;
1083 				struct tc_u32_key keys[4];
1084 			} sel2;
1085 			memset(&sel2, 0, sizeof(sel2));
1086 			NEXT_ARG();
1087 			if (parse_selector(&argc, &argv, &sel2.sel, n)) {
1088 				fprintf(stderr, "Illegal \"sample\"\n");
1089 				return -1;
1090 			}
1091 			if (sel2.sel.nkeys != 1) {
1092 				fprintf(stderr, "\"sample\" must contain"
1093 					" exactly ONE key.\n");
1094 				return -1;
1095 			}
1096 			if (*argv != 0 && strcmp(*argv, "divisor") == 0) {
1097 				NEXT_ARG();
1098 				if (get_unsigned(&divisor, *argv, 0) || divisor == 0 ||
1099 				    divisor > 0x100 || ((divisor - 1) & divisor)) {
1100 					fprintf(stderr, "Illegal sample \"divisor\"\n");
1101 					return -1;
1102 				}
1103 				NEXT_ARG();
1104 			}
1105 			hash = sel2.sel.keys[0].val&sel2.sel.keys[0].mask;
1106 			hash ^= hash>>16;
1107 			hash ^= hash>>8;
1108 			htid = ((hash%divisor)<<12)|(htid&0xFFF00000);
1109 			sample_ok = 1;
1110 			continue;
1111 		} else if (strcmp(*argv, "indev") == 0) {
1112 			char ind[IFNAMSIZ + 1];
1113 			memset(ind, 0, sizeof (ind));
1114 			argc--;
1115 			argv++;
1116 			if (argc < 1) {
1117 				fprintf(stderr, "Illegal indev\n");
1118 				return -1;
1119 			}
1120 			strncpy(ind, *argv, sizeof (ind) - 1);
1121 			addattr_l(n, MAX_MSG, TCA_U32_INDEV, ind, strlen(ind) + 1);
1122 
1123 		} else if (matches(*argv, "action") == 0) {
1124 			NEXT_ARG();
1125 			if (parse_action(&argc, &argv, TCA_U32_ACT, n)) {
1126 				fprintf(stderr, "Illegal \"action\"\n");
1127 				return -1;
1128 			}
1129 			terminal_ok++;
1130 			continue;
1131 
1132 		} else if (matches(*argv, "police") == 0) {
1133 			NEXT_ARG();
1134 			if (parse_police(&argc, &argv, TCA_U32_POLICE, n)) {
1135 				fprintf(stderr, "Illegal \"police\"\n");
1136 				return -1;
1137 			}
1138 			terminal_ok++;
1139 			continue;
1140 		} else if (strcmp(*argv, "help") == 0) {
1141 			explain();
1142 			return -1;
1143 		} else {
1144 			fprintf(stderr, "What is \"%s\"?\n", *argv);
1145 			explain();
1146 			return -1;
1147 		}
1148 		argc--; argv++;
1149 	}
1150 
1151 	/* We dont necessarily need class/flowids */
1152 	if (terminal_ok)
1153 		sel.sel.flags |= TC_U32_TERMINAL;
1154 
1155 	if (order) {
1156 		if (TC_U32_NODE(t->tcm_handle) && order != TC_U32_NODE(t->tcm_handle)) {
1157 			fprintf(stderr, "\"order\" contradicts \"handle\"\n");
1158 			return -1;
1159 		}
1160 		t->tcm_handle |= order;
1161 	}
1162 
1163 	if (htid)
1164 		addattr_l(n, MAX_MSG, TCA_U32_HASH, &htid, 4);
1165 	if (sel_ok)
1166 		addattr_l(n, MAX_MSG, TCA_U32_SEL, &sel,
1167 			  sizeof(sel.sel)+sel.sel.nkeys*sizeof(struct tc_u32_key));
1168 	tail->rta_len = (void *) NLMSG_TAIL(n) - (void *) tail;
1169 	return 0;
1170 }
1171 
u32_print_opt(struct filter_util * qu,FILE * f,struct rtattr * opt,__u32 handle)1172 static int u32_print_opt(struct filter_util *qu, FILE *f, struct rtattr *opt,
1173 			 __u32 handle)
1174 {
1175 	struct rtattr *tb[TCA_U32_MAX+1];
1176 	struct tc_u32_sel *sel = NULL;
1177 	struct tc_u32_pcnt *pf = NULL;
1178 
1179 	if (opt == NULL)
1180 		return 0;
1181 
1182 	parse_rtattr_nested(tb, TCA_U32_MAX, opt);
1183 
1184 	if (handle) {
1185 		SPRINT_BUF(b1);
1186 		fprintf(f, "fh %s ", sprint_u32_handle(handle, b1));
1187 	}
1188 	if (TC_U32_NODE(handle)) {
1189 		fprintf(f, "order %d ", TC_U32_NODE(handle));
1190 	}
1191 
1192 	if (tb[TCA_U32_SEL]) {
1193 		if (RTA_PAYLOAD(tb[TCA_U32_SEL])  < sizeof(*sel))
1194 			return -1;
1195 
1196 		sel = RTA_DATA(tb[TCA_U32_SEL]);
1197 	}
1198 
1199 	if (tb[TCA_U32_DIVISOR]) {
1200 		fprintf(f, "ht divisor %d ", *(__u32*)RTA_DATA(tb[TCA_U32_DIVISOR]));
1201 	} else if (tb[TCA_U32_HASH]) {
1202 		__u32 htid = *(__u32*)RTA_DATA(tb[TCA_U32_HASH]);
1203 		fprintf(f, "key ht %x bkt %x ", TC_U32_USERHTID(htid),
1204 			TC_U32_HASH(htid));
1205 	} else {
1206 		fprintf(f, "??? ");
1207 	}
1208 	if (tb[TCA_U32_CLASSID]) {
1209 		SPRINT_BUF(b1);
1210 		fprintf(f, "%sflowid %s ",
1211 			!sel || !(sel->flags&TC_U32_TERMINAL) ? "*" : "",
1212 			sprint_tc_classid(*(__u32*)RTA_DATA(tb[TCA_U32_CLASSID]), b1));
1213 	} else if (sel && sel->flags&TC_U32_TERMINAL) {
1214 		fprintf(f, "terminal flowid ??? ");
1215 	}
1216 	if (tb[TCA_U32_LINK]) {
1217 		SPRINT_BUF(b1);
1218 		fprintf(f, "link %s ",
1219 			sprint_u32_handle(*(__u32*)RTA_DATA(tb[TCA_U32_LINK]), b1));
1220 	}
1221 
1222 	if (tb[TCA_U32_PCNT]) {
1223 		if (RTA_PAYLOAD(tb[TCA_U32_PCNT])  < sizeof(*pf)) {
1224 			fprintf(f, "Broken perf counters \n");
1225 			return -1;
1226 		}
1227 		pf = RTA_DATA(tb[TCA_U32_PCNT]);
1228 	}
1229 
1230 	if (sel && show_stats && NULL != pf)
1231 		fprintf(f, " (rule hit %llu success %llu)",
1232 			(unsigned long long) pf->rcnt,
1233 			(unsigned long long) pf->rhit);
1234 
1235 	if (tb[TCA_U32_MARK]) {
1236 		struct tc_u32_mark *mark = RTA_DATA(tb[TCA_U32_MARK]);
1237 		if (RTA_PAYLOAD(tb[TCA_U32_MARK]) < sizeof(*mark)) {
1238 			fprintf(f, "\n  Invalid mark (kernel&iproute2 mismatch)\n");
1239 		} else {
1240 			fprintf(f, "\n  mark 0x%04x 0x%04x (success %d)",
1241 				mark->val, mark->mask, mark->success);
1242 		}
1243 	}
1244 
1245 	if (sel) {
1246 		if (sel->nkeys) {
1247 			int i;
1248 			for (i=0; i<sel->nkeys; i++) {
1249 				show_keys(f, sel->keys + i);
1250 				if (show_stats && NULL != pf)
1251 					fprintf(f, " (success %llu ) ",
1252 						(unsigned long long) pf->kcnts[i]);
1253 			}
1254 		}
1255 
1256 		if (sel->flags&(TC_U32_VAROFFSET|TC_U32_OFFSET)) {
1257 			fprintf(f, "\n    offset ");
1258 			if (sel->flags&TC_U32_VAROFFSET)
1259 				fprintf(f, "%04x>>%d at %d ",
1260 					ntohs(sel->offmask),
1261 					sel->offshift,  sel->offoff);
1262 			if (sel->off)
1263 				fprintf(f, "plus %d ", sel->off);
1264 		}
1265 		if (sel->flags&TC_U32_EAT)
1266 			fprintf(f, " eat ");
1267 
1268 		if (sel->hmask) {
1269 			fprintf(f, "\n    hash mask %08x at %d ",
1270 				(unsigned int)htonl(sel->hmask), sel->hoff);
1271 		}
1272 	}
1273 
1274 	if (tb[TCA_U32_POLICE]) {
1275 		fprintf(f, "\n");
1276 		tc_print_police(f, tb[TCA_U32_POLICE]);
1277 	}
1278 	if (tb[TCA_U32_INDEV]) {
1279 		struct rtattr *idev = tb[TCA_U32_INDEV];
1280 		fprintf(f, "\n  input dev %s\n", (char *) RTA_DATA(idev));
1281 	}
1282 	if (tb[TCA_U32_ACT]) {
1283 		tc_print_action(f, tb[TCA_U32_ACT]);
1284 	}
1285 
1286 	return 0;
1287 }
1288 
1289 struct filter_util u32_filter_util = {
1290 	.id = "u32",
1291 	.parse_fopt = u32_parse_opt,
1292 	.print_fopt = u32_print_opt,
1293 };
1294