mirror of
				https://git.proxmox.com/git/mirror_iproute2
				synced 2025-10-30 03:34:18 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			176 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			176 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * em_u32.c		U32 Ematch
 | |
|  *
 | |
|  *		This program is free software; you can distribute it and/or
 | |
|  *		modify it under the terms of the GNU General Public License
 | |
|  *		as published by the Free Software Foundation; either version
 | |
|  *		2 of the License, or (at your option) any later version.
 | |
|  *
 | |
|  * Authors:	Thomas Graf <tgraf@suug.ch>
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/socket.h>
 | |
| #include <netinet/in.h>
 | |
| #include <arpa/inet.h>
 | |
| #include <string.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #include "m_ematch.h"
 | |
| 
 | |
| extern struct ematch_util u32_ematch_util;
 | |
| 
 | |
| static void u32_print_usage(FILE *fd)
 | |
| {
 | |
| 	fprintf(fd,
 | |
| 	    "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
 | |
| 	    "where: ALIGN  := { u8 | u16 | u32 }\n" \
 | |
| 	    "\n" \
 | |
| 	    "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
 | |
| }
 | |
| 
 | |
| static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
 | |
| 			  struct bstr *args)
 | |
| {
 | |
| 	struct bstr *a;
 | |
| 	int align, nh_len;
 | |
| 	unsigned long key, mask, offmask = 0, offset;
 | |
| 	struct tc_u32_key u_key = {};
 | |
| 
 | |
| #define PARSE_ERR(CARG, FMT, ARGS...) \
 | |
| 	em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT, ##ARGS)
 | |
| 
 | |
| 	if (args == NULL)
 | |
| 		return PARSE_ERR(args, "u32: missing arguments");
 | |
| 
 | |
| 	if (!bstrcmp(args, "u8"))
 | |
| 		align = 1;
 | |
| 	else if (!bstrcmp(args, "u16"))
 | |
| 		align = 2;
 | |
| 	else if (!bstrcmp(args, "u32"))
 | |
| 		align = 4;
 | |
| 	else
 | |
| 		return PARSE_ERR(args, "u32: invalid alignment");
 | |
| 
 | |
| 	a = bstr_next(args);
 | |
| 	if (a == NULL)
 | |
| 		return PARSE_ERR(a, "u32: missing key");
 | |
| 
 | |
| 	key = bstrtoul(a);
 | |
| 	if (key == ULONG_MAX)
 | |
| 		return PARSE_ERR(a, "u32: invalid key, must be numeric");
 | |
| 
 | |
| 	a = bstr_next(a);
 | |
| 	if (a == NULL)
 | |
| 		return PARSE_ERR(a, "u32: missing mask");
 | |
| 
 | |
| 	mask = bstrtoul(a);
 | |
| 	if (mask == ULONG_MAX)
 | |
| 		return PARSE_ERR(a, "u32: invalid mask, must be numeric");
 | |
| 
 | |
| 	a = bstr_next(a);
 | |
| 	if (a == NULL || bstrcmp(a, "at") != 0)
 | |
| 		return PARSE_ERR(a, "u32: missing \"at\"");
 | |
| 
 | |
| 	a = bstr_next(a);
 | |
| 	if (a == NULL)
 | |
| 		return PARSE_ERR(a, "u32: missing offset");
 | |
| 
 | |
| 	nh_len = strlen("nexthdr+");
 | |
| 	if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
 | |
| 		char buf[a->len - nh_len + 1];
 | |
| 
 | |
| 		offmask = -1;
 | |
| 		memcpy(buf, a->data + nh_len, a->len - nh_len);
 | |
| 		offset = strtoul(buf, NULL, 0);
 | |
| 	} else if (!bstrcmp(a, "nexthdr+")) {
 | |
| 		a = bstr_next(a);
 | |
| 		if (a == NULL)
 | |
| 			return PARSE_ERR(a, "u32: missing offset");
 | |
| 		offset = bstrtoul(a);
 | |
| 	} else
 | |
| 		offset = bstrtoul(a);
 | |
| 
 | |
| 	if (offset == ULONG_MAX)
 | |
| 		return PARSE_ERR(a, "u32: invalid offset");
 | |
| 
 | |
| 	if (a->next)
 | |
| 		return PARSE_ERR(a->next, "u32: unexpected trailer");
 | |
| 
 | |
| 	switch (align) {
 | |
| 		case 1:
 | |
| 			if (key > 0xFF)
 | |
| 				return PARSE_ERR(a, "Illegal key (>0xFF)");
 | |
| 			if (mask > 0xFF)
 | |
| 				return PARSE_ERR(a, "Illegal mask (>0xFF)");
 | |
| 
 | |
| 			key <<= 24 - ((offset & 3) * 8);
 | |
| 			mask <<= 24 - ((offset & 3) * 8);
 | |
| 			offset &= ~3;
 | |
| 			break;
 | |
| 
 | |
| 		case 2:
 | |
| 			if (key > 0xFFFF)
 | |
| 				return PARSE_ERR(a, "Illegal key (>0xFFFF)");
 | |
| 			if (mask > 0xFFFF)
 | |
| 				return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
 | |
| 
 | |
| 			if ((offset & 3) == 0) {
 | |
| 				key <<= 16;
 | |
| 				mask <<= 16;
 | |
| 			}
 | |
| 			offset &= ~3;
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	key = htonl(key);
 | |
| 	mask = htonl(mask);
 | |
| 
 | |
| 	if (offset % 4)
 | |
| 		return PARSE_ERR(a, "u32: invalid offset alignment, " \
 | |
| 		    "must be aligned to 4.");
 | |
| 
 | |
| 	key &= mask;
 | |
| 
 | |
| 	u_key.mask = mask;
 | |
| 	u_key.val = key;
 | |
| 	u_key.off = offset;
 | |
| 	u_key.offmask = offmask;
 | |
| 
 | |
| 	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
 | |
| 	addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
 | |
| 
 | |
| #undef PARSE_ERR
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
 | |
| 			  int data_len)
 | |
| {
 | |
| 	struct tc_u32_key *u_key = data;
 | |
| 
 | |
| 	if (data_len < sizeof(*u_key)) {
 | |
| 		fprintf(stderr, "U32 header size mismatch\n");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	fprintf(fd, "%08x/%08x at %s%d",
 | |
| 	    (unsigned int) ntohl(u_key->val),
 | |
| 	    (unsigned int) ntohl(u_key->mask),
 | |
| 	    u_key->offmask ? "nexthdr+" : "",
 | |
| 	    u_key->off);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| struct ematch_util u32_ematch_util = {
 | |
| 	.kind = "u32",
 | |
| 	.kind_num = TCF_EM_U32,
 | |
| 	.parse_eopt = u32_parse_eopt,
 | |
| 	.print_eopt = u32_print_eopt,
 | |
| 	.print_usage = u32_print_usage
 | |
| };
 | 
