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