2 * em_ipset.c IPset Ematch
4 * (C) 2012 Florian Westphal <fw@strlen.de>
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>
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.
27 #include <linux/netfilter/ipset/ip_set.h>
29 #ifndef IPSET_INVALID_ID
30 typedef __u16 ip_set_id_t
;
39 #endif /* IPSET_INVALID_ID */
41 #include <linux/netfilter/xt_set.h>
44 #ifndef IPSET_INVALID_ID
45 #define IPSET_INVALID_ID 65535
48 union ip_set_name_index
{
49 char name
[IPSET_MAXNAMELEN
];
53 #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
54 struct ip_set_req_get_set
{
57 union ip_set_name_index set
;
60 #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
61 /* Uses ip_set_req_get_set */
63 #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
64 struct ip_set_req_version
{
68 #endif /* IPSET_INVALID_ID */
70 extern struct ematch_util ipset_ematch_util
;
72 static int get_version(unsigned *version
)
74 int res
, sockfd
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
);
75 struct ip_set_req_version req_version
;
76 socklen_t size
= sizeof(req_version
);
79 fputs("Can't open socket to ipset.\n", stderr
);
83 req_version
.op
= IP_SET_OP_VERSION
;
84 res
= getsockopt(sockfd
, SOL_IP
, SO_IP_SET
, &req_version
, &size
);
86 perror("xt_set getsockopt");
90 *version
= req_version
.version
;
94 static int do_getsockopt(struct ip_set_req_get_set
*req
)
97 socklen_t size
= sizeof(struct ip_set_req_get_set
);
98 sockfd
= get_version(&req
->version
);
101 res
= getsockopt(sockfd
, SOL_IP
, SO_IP_SET
, req
, &size
);
103 perror("Problem when communicating with ipset");
108 if (size
!= sizeof(struct ip_set_req_get_set
)) {
110 "Incorrect return size from kernel during ipset lookup, "
111 "(want %zu, got %zu)\n",
112 sizeof(struct ip_set_req_get_set
), (size_t)size
);
120 get_set_byid(char *setname
, unsigned int idx
)
122 struct ip_set_req_get_set req
;
125 req
.op
= IP_SET_OP_GET_BYINDEX
;
127 res
= do_getsockopt(&req
);
130 if (req
.set
.name
[0] == '\0') {
132 "Set with index %i in kernel doesn't exist.\n", idx
);
136 strncpy(setname
, req
.set
.name
, IPSET_MAXNAMELEN
);
141 get_set_byname(const char *setname
, struct xt_set_info
*info
)
143 struct ip_set_req_get_set req
;
146 req
.op
= IP_SET_OP_GET_BYNAME
;
147 strncpy(req
.set
.name
, setname
, IPSET_MAXNAMELEN
);
148 req
.set
.name
[IPSET_MAXNAMELEN
- 1] = '\0';
149 res
= do_getsockopt(&req
);
152 if (req
.set
.index
== IPSET_INVALID_ID
)
154 info
->index
= req
.set
.index
;
159 parse_dirs(const char *opt_arg
, struct xt_set_info
*info
)
161 char *saved
= strdup(opt_arg
);
162 char *ptr
, *tmp
= saved
;
169 while (info
->dim
< IPSET_DIM_MAX
&& tmp
!= NULL
) {
171 ptr
= strsep(&tmp
, ",");
172 if (strncmp(ptr
, "src", 3) == 0)
173 info
->flags
|= (1 << info
->dim
);
174 else if (strncmp(ptr
, "dst", 3) != 0) {
175 fputs("You must specify (the comma separated list of) 'src' or 'dst'\n", stderr
);
182 fprintf(stderr
, "Can't be more src/dst options than %u", IPSET_DIM_MAX
);
187 static void ipset_print_usage(FILE *fd
)
190 "Usage: ipset(SETNAME FLAGS)\n" \
191 "where: SETNAME:= string\n" \
192 " FLAGS := { FLAG[,FLAGS] }\n" \
193 " FLAG := { src | dst }\n" \
195 "Example: 'ipset(bulk src,dst)'\n");
198 static int ipset_parse_eopt(struct nlmsghdr
*n
, struct tcf_ematch_hdr
*hdr
,
201 struct xt_set_info set_info
;
204 memset(&set_info
, 0, sizeof(set_info
));
206 #define PARSE_ERR(CARG, FMT, ARGS...) \
207 em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT ,##ARGS)
210 return PARSE_ERR(args
, "ipset: missing set name");
212 if (args
->len
>= IPSET_MAXNAMELEN
)
213 return PARSE_ERR(args
, "ipset: set name too long (max %u)", IPSET_MAXNAMELEN
- 1);
214 ret
= get_set_byname(args
->data
, &set_info
);
216 return PARSE_ERR(args
, "ipset: unknown set name '%s'", args
->data
);
218 if (args
->next
== NULL
)
219 return PARSE_ERR(args
, "ipset: missing set flags");
221 args
= bstr_next(args
);
222 if (parse_dirs(args
->data
, &set_info
))
223 return PARSE_ERR(args
, "ipset: error parsing set flags");
226 args
= bstr_next(args
);
227 return PARSE_ERR(args
, "ipset: unknown parameter");
230 addraw_l(n
, MAX_MSG
, hdr
, sizeof(*hdr
));
231 addraw_l(n
, MAX_MSG
, &set_info
, sizeof(set_info
));
237 static int ipset_print_eopt(FILE *fd
, struct tcf_ematch_hdr
*hdr
, void *data
,
241 char setname
[IPSET_MAXNAMELEN
];
242 const struct xt_set_info
*set_info
= data
;
244 if (data_len
!= sizeof(*set_info
)) {
245 fprintf(stderr
, "xt_set_info struct size mismatch\n");
249 if (get_set_byid(setname
, set_info
->index
))
252 for (i
= 1; i
<= set_info
->dim
; i
++) {
253 fprintf(fd
, "%s%s", i
== 1 ? " " : ",", set_info
->flags
& (1 << i
) ? "src" : "dst");
259 struct ematch_util ipset_ematch_util
= {
261 .kind_num
= TCF_EM_IPSET
,
262 .parse_eopt
= ipset_parse_eopt
,
263 .print_eopt
= ipset_print_eopt
,
264 .print_usage
= ipset_print_usage