1/*
2 * em_u32.c		U32 Ematch
3 *
4 *		This program is free software; you can distribute 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:	Thomas Graf <tgraf@suug.ch>
10 */
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <fcntl.h>
17#include <sys/socket.h>
18#include <netinet/in.h>
19#include <arpa/inet.h>
20#include <string.h>
21#include <dlfcn.h>
22#include <errno.h>
23
24#include "m_ematch.h"
25
26extern struct ematch_util u32_ematch_util;
27
28static void u32_print_usage(FILE *fd)
29{
30	fprintf(fd,
31	    "Usage: u32(ALIGN VALUE MASK at [ nexthdr+ ] OFFSET)\n" \
32	    "where: ALIGN  := { u8 | u16 | u32 }\n" \
33	    "\n" \
34	    "Example: u32(u16 0x1122 0xffff at nexthdr+4)\n");
35}
36
37static int u32_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
38			  struct bstr *args)
39{
40	struct bstr *a;
41	int align, nh_len;
42	unsigned long key, mask, offmask = 0, offset;
43	struct tc_u32_key u_key;
44
45	memset(&u_key, 0, sizeof(u_key));
46
47#define PARSE_ERR(CARG, FMT, ARGS...) \
48	em_parse_error(EINVAL, args, CARG, &u32_ematch_util, FMT ,##ARGS)
49
50	if (args == NULL)
51		return PARSE_ERR(args, "u32: missing arguments");
52
53	if (!bstrcmp(args, "u8"))
54		align = 1;
55	else if (!bstrcmp(args, "u16"))
56		align = 2;
57	else if (!bstrcmp(args, "u32"))
58		align = 4;
59	else
60		return PARSE_ERR(args, "u32: invalid alignment");
61
62	a = bstr_next(args);
63	if (a == NULL)
64		return PARSE_ERR(a, "u32: missing key");
65
66	key = bstrtoul(a);
67	if (key == ULONG_MAX)
68		return PARSE_ERR(a, "u32: invalid key, must be numeric");
69
70	a = bstr_next(a);
71	if (a == NULL)
72		return PARSE_ERR(a, "u32: missing mask");
73
74	mask = bstrtoul(a);
75	if (mask == ULONG_MAX)
76		return PARSE_ERR(a, "u32: invalid mask, must be numeric");
77
78	a = bstr_next(a);
79	if (a == NULL || bstrcmp(a, "at") != 0)
80		return PARSE_ERR(a, "u32: missing \"at\"");
81
82	a = bstr_next(a);
83	if (a == NULL)
84		return PARSE_ERR(a, "u32: missing offset");
85
86	nh_len = strlen("nexthdr+");
87	if (a->len > nh_len && !memcmp(a->data, "nexthdr+", nh_len)) {
88		char buf[a->len - nh_len + 1];
89		offmask = -1;
90		memcpy(buf, a->data + nh_len, a->len - nh_len);
91		offset = strtoul(buf, NULL, 0);
92	} else if (!bstrcmp(a, "nexthdr+")) {
93		a = bstr_next(a);
94		if (a == NULL)
95			return PARSE_ERR(a, "u32: missing offset");
96		offset = bstrtoul(a);
97	} else
98		offset = bstrtoul(a);
99
100	if (offset == ULONG_MAX)
101		return PARSE_ERR(a, "u32: invalid offset");
102
103	if (a->next)
104		return PARSE_ERR(a->next, "u32: unexpected trailer");
105
106	switch (align) {
107		case 1:
108			if (key > 0xFF)
109				return PARSE_ERR(a, "Illegal key (>0xFF)");
110			if (mask > 0xFF)
111				return PARSE_ERR(a, "Illegal mask (>0xFF)");
112
113			key <<= 24 - ((offset & 3) * 8);
114			mask <<= 24 - ((offset & 3) * 8);
115			offset &= ~3;
116			break;
117
118		case 2:
119			if (key > 0xFFFF)
120				return PARSE_ERR(a, "Illegal key (>0xFFFF)");
121			if (mask > 0xFFFF)
122				return PARSE_ERR(a, "Illegal mask (>0xFFFF)");
123
124			if ((offset & 3) == 0) {
125				key <<= 16;
126				mask <<= 16;
127			}
128			offset &= ~3;
129			break;
130	}
131
132	key = htonl(key);
133	mask = htonl(mask);
134
135	if (offset % 4)
136		return PARSE_ERR(a, "u32: invalid offset alignment, " \
137		    "must be aligned to 4.");
138
139	key &= mask;
140
141	u_key.mask = mask;
142	u_key.val = key;
143	u_key.off = offset;
144	u_key.offmask = offmask;
145
146	addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
147	addraw_l(n, MAX_MSG, &u_key, sizeof(u_key));
148
149#undef PARSE_ERR
150	return 0;
151}
152
153static int u32_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
154			  int data_len)
155{
156	struct tc_u32_key *u_key = data;
157
158	if (data_len < sizeof(*u_key)) {
159		fprintf(stderr, "U32 header size mismatch\n");
160		return -1;
161	}
162
163	fprintf(fd, "%08x/%08x at %s%d",
164	    (unsigned int) ntohl(u_key->val),
165	    (unsigned int) ntohl(u_key->mask),
166	    u_key->offmask ? "nexthdr+" : "",
167	    u_key->off);
168
169	return 0;
170}
171
172struct ematch_util u32_ematch_util = {
173	.kind = "u32",
174	.kind_num = TCF_EM_U32,
175	.parse_eopt = u32_parse_eopt,
176	.print_eopt = u32_print_eopt,
177	.print_usage = u32_print_usage
178};
179