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