]>
Commit | Line | Data |
---|---|---|
8194411a FW |
1 | /* |
2 | * em_ipset.c IPset Ematch | |
3 | * | |
4 | * (C) 2012 Florian Westphal <fw@strlen.de> | |
5 | * | |
6 | * Parts taken from iptables libxt_set.h: | |
7 | * Copyright (C) 2000-2002 Joakim Axelsson <gozem@linux.nu> | |
8 | * Patrick Schaaf <bof@bof.de> | |
9 | * Martin Josefsson <gandalf@wlug.westbo.se> | |
10 | * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec@blackhole.kfki.hu> | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or modify | |
13 | * it under the terms of the GNU General Public License version 2 as | |
14 | * published by the Free Software Foundation. | |
15 | */ | |
16 | ||
17 | #include <stdbool.h> | |
18 | #include <stdio.h> | |
19 | #include <errno.h> | |
20 | #include <netdb.h> | |
21 | #include <unistd.h> | |
22 | #include <string.h> | |
508f3c23 LB |
23 | #ifdef HAVE_LIBBSD |
24 | #include <bsd/string.h> | |
25 | #endif | |
8194411a FW |
26 | #include <stdlib.h> |
27 | #include <getopt.h> | |
28 | ||
29 | #include <xtables.h> | |
30 | #include <linux/netfilter/ipset/ip_set.h> | |
31 | ||
32 | #ifndef IPSET_INVALID_ID | |
33 | typedef __u16 ip_set_id_t; | |
34 | ||
35 | enum ip_set_dim { | |
36 | IPSET_DIM_ZERO = 0, | |
37 | IPSET_DIM_ONE, | |
38 | IPSET_DIM_TWO, | |
39 | IPSET_DIM_THREE, | |
40 | IPSET_DIM_MAX = 6, | |
41 | }; | |
42 | #endif /* IPSET_INVALID_ID */ | |
43 | ||
44 | #include <linux/netfilter/xt_set.h> | |
45 | #include "m_ematch.h" | |
46 | ||
47 | #ifndef IPSET_INVALID_ID | |
48 | #define IPSET_INVALID_ID 65535 | |
49 | #define SO_IP_SET 83 | |
50 | ||
51 | union ip_set_name_index { | |
52 | char name[IPSET_MAXNAMELEN]; | |
53 | __u16 index; | |
54 | }; | |
55 | ||
56 | #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */ | |
57 | struct ip_set_req_get_set { | |
32a121cb SH |
58 | unsigned int op; |
59 | unsigned int version; | |
8194411a FW |
60 | union ip_set_name_index set; |
61 | }; | |
62 | ||
63 | #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */ | |
64 | /* Uses ip_set_req_get_set */ | |
65 | ||
66 | #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */ | |
67 | struct ip_set_req_version { | |
32a121cb SH |
68 | unsigned int op; |
69 | unsigned int version; | |
8194411a FW |
70 | }; |
71 | #endif /* IPSET_INVALID_ID */ | |
72 | ||
73 | extern struct ematch_util ipset_ematch_util; | |
74 | ||
32a121cb | 75 | static int get_version(unsigned int *version) |
8194411a FW |
76 | { |
77 | int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); | |
78 | struct ip_set_req_version req_version; | |
79 | socklen_t size = sizeof(req_version); | |
80 | ||
81 | if (sockfd < 0) { | |
82 | fputs("Can't open socket to ipset.\n", stderr); | |
83 | return -1; | |
84 | } | |
85 | ||
86 | req_version.op = IP_SET_OP_VERSION; | |
87 | res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size); | |
88 | if (res != 0) { | |
89 | perror("xt_set getsockopt"); | |
3e587d9f | 90 | close(sockfd); |
8194411a FW |
91 | return -1; |
92 | } | |
93 | ||
94 | *version = req_version.version; | |
95 | return sockfd; | |
96 | } | |
97 | ||
98 | static int do_getsockopt(struct ip_set_req_get_set *req) | |
99 | { | |
100 | int sockfd, res; | |
101 | socklen_t size = sizeof(struct ip_set_req_get_set); | |
32a121cb | 102 | |
8194411a FW |
103 | sockfd = get_version(&req->version); |
104 | if (sockfd < 0) | |
105 | return -1; | |
106 | res = getsockopt(sockfd, SOL_IP, SO_IP_SET, req, &size); | |
107 | if (res != 0) | |
108 | perror("Problem when communicating with ipset"); | |
109 | close(sockfd); | |
110 | if (res != 0) | |
111 | return -1; | |
112 | ||
113 | if (size != sizeof(struct ip_set_req_get_set)) { | |
114 | fprintf(stderr, | |
32a121cb | 115 | "Incorrect return size from kernel during ipset lookup, (want %zu, got %zu)\n", |
8194411a FW |
116 | sizeof(struct ip_set_req_get_set), (size_t)size); |
117 | return -1; | |
118 | } | |
119 | ||
120 | return res; | |
121 | } | |
122 | ||
123 | static int | |
124 | get_set_byid(char *setname, unsigned int idx) | |
125 | { | |
126 | struct ip_set_req_get_set req; | |
127 | int res; | |
128 | ||
129 | req.op = IP_SET_OP_GET_BYINDEX; | |
130 | req.set.index = idx; | |
131 | res = do_getsockopt(&req); | |
132 | if (res != 0) | |
133 | return -1; | |
134 | if (req.set.name[0] == '\0') { | |
135 | fprintf(stderr, | |
136 | "Set with index %i in kernel doesn't exist.\n", idx); | |
137 | return -1; | |
138 | } | |
139 | ||
140 | strncpy(setname, req.set.name, IPSET_MAXNAMELEN); | |
141 | return 0; | |
142 | } | |
143 | ||
144 | static int | |
145 | get_set_byname(const char *setname, struct xt_set_info *info) | |
146 | { | |
147 | struct ip_set_req_get_set req; | |
148 | int res; | |
149 | ||
150 | req.op = IP_SET_OP_GET_BYNAME; | |
18f156bf | 151 | strlcpy(req.set.name, setname, IPSET_MAXNAMELEN); |
8194411a FW |
152 | res = do_getsockopt(&req); |
153 | if (res != 0) | |
154 | return -1; | |
155 | if (req.set.index == IPSET_INVALID_ID) | |
156 | return -1; | |
157 | info->index = req.set.index; | |
158 | return 0; | |
159 | } | |
160 | ||
161 | static int | |
162 | parse_dirs(const char *opt_arg, struct xt_set_info *info) | |
163 | { | |
32a121cb SH |
164 | char *saved = strdup(opt_arg); |
165 | char *ptr, *tmp = saved; | |
8194411a FW |
166 | |
167 | if (!tmp) { | |
168 | perror("strdup"); | |
169 | return -1; | |
170 | } | |
171 | ||
32a121cb SH |
172 | while (info->dim < IPSET_DIM_MAX && tmp != NULL) { |
173 | info->dim++; | |
174 | ptr = strsep(&tmp, ","); | |
175 | if (strncmp(ptr, "src", 3) == 0) | |
176 | info->flags |= (1 << info->dim); | |
177 | else if (strncmp(ptr, "dst", 3) != 0) { | |
178 | fputs("You must specify (the comma separated list of) 'src' or 'dst'\n", stderr); | |
8194411a FW |
179 | free(saved); |
180 | return -1; | |
181 | } | |
32a121cb | 182 | } |
8194411a | 183 | |
32a121cb SH |
184 | if (tmp) |
185 | fprintf(stderr, "Can't be more src/dst options than %u", IPSET_DIM_MAX); | |
186 | free(saved); | |
8194411a FW |
187 | return tmp ? -1 : 0; |
188 | } | |
189 | ||
190 | static void ipset_print_usage(FILE *fd) | |
191 | { | |
192 | fprintf(fd, | |
193 | "Usage: ipset(SETNAME FLAGS)\n" \ | |
194 | "where: SETNAME:= string\n" \ | |
195 | " FLAGS := { FLAG[,FLAGS] }\n" \ | |
196 | " FLAG := { src | dst }\n" \ | |
197 | "\n" \ | |
198 | "Example: 'ipset(bulk src,dst)'\n"); | |
199 | } | |
200 | ||
201 | static int ipset_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr, | |
202 | struct bstr *args) | |
203 | { | |
d17b136f | 204 | struct xt_set_info set_info = {}; |
8194411a FW |
205 | int ret; |
206 | ||
8194411a | 207 | #define PARSE_ERR(CARG, FMT, ARGS...) \ |
32a121cb | 208 | em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT, ##ARGS) |
8194411a FW |
209 | |
210 | if (args == NULL) | |
211 | return PARSE_ERR(args, "ipset: missing set name"); | |
212 | ||
213 | if (args->len >= IPSET_MAXNAMELEN) | |
214 | return PARSE_ERR(args, "ipset: set name too long (max %u)", IPSET_MAXNAMELEN - 1); | |
215 | ret = get_set_byname(args->data, &set_info); | |
216 | if (ret < 0) | |
217 | return PARSE_ERR(args, "ipset: unknown set name '%s'", args->data); | |
218 | ||
219 | if (args->next == NULL) | |
220 | return PARSE_ERR(args, "ipset: missing set flags"); | |
221 | ||
222 | args = bstr_next(args); | |
223 | if (parse_dirs(args->data, &set_info)) | |
224 | return PARSE_ERR(args, "ipset: error parsing set flags"); | |
225 | ||
226 | if (args->next) { | |
227 | args = bstr_next(args); | |
228 | return PARSE_ERR(args, "ipset: unknown parameter"); | |
229 | } | |
230 | ||
231 | addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); | |
232 | addraw_l(n, MAX_MSG, &set_info, sizeof(set_info)); | |
233 | ||
234 | #undef PARSE_ERR | |
235 | return 0; | |
236 | } | |
237 | ||
238 | static int ipset_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, | |
239 | int data_len) | |
240 | { | |
241 | int i; | |
32a121cb | 242 | char setname[IPSET_MAXNAMELEN]; |
8194411a FW |
243 | const struct xt_set_info *set_info = data; |
244 | ||
245 | if (data_len != sizeof(*set_info)) { | |
246 | fprintf(stderr, "xt_set_info struct size mismatch\n"); | |
247 | return -1; | |
248 | } | |
249 | ||
32a121cb | 250 | if (get_set_byid(setname, set_info->index)) |
8194411a FW |
251 | return -1; |
252 | fputs(setname, fd); | |
253 | for (i = 1; i <= set_info->dim; i++) { | |
254 | fprintf(fd, "%s%s", i == 1 ? " " : ",", set_info->flags & (1 << i) ? "src" : "dst"); | |
255 | } | |
256 | ||
257 | return 0; | |
258 | } | |
259 | ||
260 | struct ematch_util ipset_ematch_util = { | |
261 | .kind = "ipset", | |
262 | .kind_num = TCF_EM_IPSET, | |
263 | .parse_eopt = ipset_parse_eopt, | |
264 | .print_eopt = ipset_print_eopt, | |
265 | .print_usage = ipset_print_usage | |
266 | }; |