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.
24 #include <bsd/string.h>
30 #include <linux/netfilter/ipset/ip_set.h>
32 #ifndef IPSET_INVALID_ID
33 typedef __u16 ip_set_id_t
;
42 #endif /* IPSET_INVALID_ID */
44 #include <linux/netfilter/xt_set.h>
47 #ifndef IPSET_INVALID_ID
48 #define IPSET_INVALID_ID 65535
51 union ip_set_name_index
{
52 char name
[IPSET_MAXNAMELEN
];
56 #define IP_SET_OP_GET_BYNAME 0x00000006 /* Get set index by name */
57 struct ip_set_req_get_set
{
60 union ip_set_name_index set
;
63 #define IP_SET_OP_GET_BYINDEX 0x00000007 /* Get set name by index */
64 /* Uses ip_set_req_get_set */
66 #define IP_SET_OP_VERSION 0x00000100 /* Ask kernel version */
67 struct ip_set_req_version
{
71 #endif /* IPSET_INVALID_ID */
73 extern struct ematch_util ipset_ematch_util
;
75 static int get_version(unsigned int *version
)
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
);
82 fputs("Can't open socket to ipset.\n", stderr
);
86 req_version
.op
= IP_SET_OP_VERSION
;
87 res
= getsockopt(sockfd
, SOL_IP
, SO_IP_SET
, &req_version
, &size
);
89 perror("xt_set getsockopt");
94 *version
= req_version
.version
;
98 static int do_getsockopt(struct ip_set_req_get_set
*req
)
101 socklen_t size
= sizeof(struct ip_set_req_get_set
);
103 sockfd
= get_version(&req
->version
);
106 res
= getsockopt(sockfd
, SOL_IP
, SO_IP_SET
, req
, &size
);
108 perror("Problem when communicating with ipset");
113 if (size
!= sizeof(struct ip_set_req_get_set
)) {
115 "Incorrect return size from kernel during ipset lookup, (want %zu, got %zu)\n",
116 sizeof(struct ip_set_req_get_set
), (size_t)size
);
124 get_set_byid(char *setname
, unsigned int idx
)
126 struct ip_set_req_get_set req
;
129 req
.op
= IP_SET_OP_GET_BYINDEX
;
131 res
= do_getsockopt(&req
);
134 if (req
.set
.name
[0] == '\0') {
136 "Set with index %i in kernel doesn't exist.\n", idx
);
140 strncpy(setname
, req
.set
.name
, IPSET_MAXNAMELEN
);
145 get_set_byname(const char *setname
, struct xt_set_info
*info
)
147 struct ip_set_req_get_set req
;
150 req
.op
= IP_SET_OP_GET_BYNAME
;
151 strlcpy(req
.set
.name
, setname
, IPSET_MAXNAMELEN
);
152 res
= do_getsockopt(&req
);
155 if (req
.set
.index
== IPSET_INVALID_ID
)
157 info
->index
= req
.set
.index
;
162 parse_dirs(const char *opt_arg
, struct xt_set_info
*info
)
164 char *saved
= strdup(opt_arg
);
165 char *ptr
, *tmp
= saved
;
172 while (info
->dim
< IPSET_DIM_MAX
&& tmp
!= NULL
) {
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
);
185 fprintf(stderr
, "Can't be more src/dst options than %u", IPSET_DIM_MAX
);
190 static void ipset_print_usage(FILE *fd
)
193 "Usage: ipset(SETNAME FLAGS)\n" \
194 "where: SETNAME:= string\n" \
195 " FLAGS := { FLAG[,FLAGS] }\n" \
196 " FLAG := { src | dst }\n" \
198 "Example: 'ipset(bulk src,dst)'\n");
201 static int ipset_parse_eopt(struct nlmsghdr
*n
, struct tcf_ematch_hdr
*hdr
,
204 struct xt_set_info set_info
= {};
207 #define PARSE_ERR(CARG, FMT, ARGS...) \
208 em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT, ##ARGS)
211 return PARSE_ERR(args
, "ipset: missing set name");
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
);
217 return PARSE_ERR(args
, "ipset: unknown set name '%s'", args
->data
);
219 if (args
->next
== NULL
)
220 return PARSE_ERR(args
, "ipset: missing set flags");
222 args
= bstr_next(args
);
223 if (parse_dirs(args
->data
, &set_info
))
224 return PARSE_ERR(args
, "ipset: error parsing set flags");
227 args
= bstr_next(args
);
228 return PARSE_ERR(args
, "ipset: unknown parameter");
231 addraw_l(n
, MAX_MSG
, hdr
, sizeof(*hdr
));
232 addraw_l(n
, MAX_MSG
, &set_info
, sizeof(set_info
));
238 static int ipset_print_eopt(FILE *fd
, struct tcf_ematch_hdr
*hdr
, void *data
,
242 char setname
[IPSET_MAXNAMELEN
];
243 const struct xt_set_info
*set_info
= data
;
245 if (data_len
!= sizeof(*set_info
)) {
246 fprintf(stderr
, "xt_set_info struct size mismatch\n");
250 if (get_set_byid(setname
, set_info
->index
))
253 for (i
= 1; i
<= set_info
->dim
; i
++) {
254 fprintf(fd
, "%s%s", i
== 1 ? " " : ",", set_info
->flags
& (1 << i
) ? "src" : "dst");
260 struct ematch_util ipset_ematch_util
= {
262 .kind_num
= TCF_EM_IPSET
,
263 .parse_eopt
= ipset_parse_eopt
,
264 .print_eopt
= ipset_print_eopt
,
265 .print_usage
= ipset_print_usage