Fix build break in 64bit architectures
[platform/upstream/iproute2.git] / tc / em_ipset.c
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>
23 #include <stdlib.h>
24 #include <getopt.h>
25
26 #include <xtables.h>
27 #include <linux/netfilter/ipset/ip_set.h>
28
29 #ifndef IPSET_INVALID_ID
30 typedef __u16 ip_set_id_t;
31
32 enum ip_set_dim {
33         IPSET_DIM_ZERO = 0,
34         IPSET_DIM_ONE,
35         IPSET_DIM_TWO,
36         IPSET_DIM_THREE,
37         IPSET_DIM_MAX = 6,
38 };
39 #endif /* IPSET_INVALID_ID */
40
41 #include <linux/netfilter/xt_set.h>
42 #include "m_ematch.h"
43
44 #ifndef IPSET_INVALID_ID
45 #define IPSET_INVALID_ID        65535
46 #define SO_IP_SET               83
47
48 union ip_set_name_index {
49         char name[IPSET_MAXNAMELEN];
50         __u16 index;
51 };
52
53 #define IP_SET_OP_GET_BYNAME    0x00000006      /* Get set index by name */
54 struct ip_set_req_get_set {
55         unsigned int op;
56         unsigned int version;
57         union ip_set_name_index set;
58 };
59
60 #define IP_SET_OP_GET_BYINDEX   0x00000007      /* Get set name by index */
61 /* Uses ip_set_req_get_set */
62
63 #define IP_SET_OP_VERSION       0x00000100      /* Ask kernel version */
64 struct ip_set_req_version {
65         unsigned int op;
66         unsigned int version;
67 };
68 #endif /* IPSET_INVALID_ID */
69
70 extern struct ematch_util ipset_ematch_util;
71
72 static int get_version(unsigned int *version)
73 {
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);
77
78         if (sockfd < 0) {
79                 fputs("Can't open socket to ipset.\n", stderr);
80                 return -1;
81         }
82
83         req_version.op = IP_SET_OP_VERSION;
84         res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
85         if (res != 0) {
86                 perror("xt_set getsockopt");
87                 close(sockfd);
88                 return -1;
89         }
90
91         *version = req_version.version;
92         return sockfd;
93 }
94
95 static int do_getsockopt(struct ip_set_req_get_set *req)
96 {
97         int sockfd, res;
98         socklen_t size = sizeof(struct ip_set_req_get_set);
99
100         sockfd = get_version(&req->version);
101         if (sockfd < 0)
102                 return -1;
103         res = getsockopt(sockfd, SOL_IP, SO_IP_SET, req, &size);
104         if (res != 0)
105                 perror("Problem when communicating with ipset");
106         close(sockfd);
107         if (res != 0)
108                 return -1;
109
110         if (size != sizeof(struct ip_set_req_get_set)) {
111                 fprintf(stderr,
112                         "Incorrect return size from kernel during ipset lookup, (want %zu, got %zu)\n",
113                         sizeof(struct ip_set_req_get_set), (size_t)size);
114                 return -1;
115         }
116
117         return res;
118 }
119
120 static int
121 get_set_byid(char *setname, unsigned int idx)
122 {
123         struct ip_set_req_get_set req;
124         int res;
125
126         req.op = IP_SET_OP_GET_BYINDEX;
127         req.set.index = idx;
128         res = do_getsockopt(&req);
129         if (res != 0)
130                 return -1;
131         if (req.set.name[0] == '\0') {
132                 fprintf(stderr,
133                         "Set with index %i in kernel doesn't exist.\n", idx);
134                 return -1;
135         }
136
137         strncpy(setname, req.set.name, IPSET_MAXNAMELEN);
138         return 0;
139 }
140
141 static int
142 get_set_byname(const char *setname, struct xt_set_info *info)
143 {
144         struct ip_set_req_get_set req;
145         int res;
146
147         req.op = IP_SET_OP_GET_BYNAME;
148         strlcpy(req.set.name, setname, IPSET_MAXNAMELEN);
149         res = do_getsockopt(&req);
150         if (res != 0)
151                 return -1;
152         if (req.set.index == IPSET_INVALID_ID)
153                 return -1;
154         info->index = req.set.index;
155         return 0;
156 }
157
158 static int
159 parse_dirs(const char *opt_arg, struct xt_set_info *info)
160 {
161         char *saved = strdup(opt_arg);
162         char *ptr, *tmp = saved;
163
164         if (!tmp) {
165                 perror("strdup");
166                 return -1;
167         }
168
169         while (info->dim < IPSET_DIM_MAX && tmp != NULL) {
170                 info->dim++;
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);
176                         free(saved);
177                         return -1;
178                 }
179         }
180
181         if (tmp)
182                 fprintf(stderr, "Can't be more src/dst options than %u", IPSET_DIM_MAX);
183         free(saved);
184         return tmp ? -1 : 0;
185 }
186
187 static void ipset_print_usage(FILE *fd)
188 {
189         fprintf(fd,
190             "Usage: ipset(SETNAME FLAGS)\n" \
191             "where: SETNAME:= string\n" \
192             "       FLAGS  := { FLAG[,FLAGS] }\n" \
193             "       FLAG   := { src | dst }\n" \
194             "\n" \
195             "Example: 'ipset(bulk src,dst)'\n");
196 }
197
198 static int ipset_parse_eopt(struct nlmsghdr *n, struct tcf_ematch_hdr *hdr,
199                             struct bstr *args)
200 {
201         struct xt_set_info set_info = {};
202         int ret;
203
204 #define PARSE_ERR(CARG, FMT, ARGS...) \
205         em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT, ##ARGS)
206
207         if (args == NULL)
208                 return PARSE_ERR(args, "ipset: missing set name");
209
210         if (args->len >= IPSET_MAXNAMELEN)
211                 return PARSE_ERR(args, "ipset: set name too long (max %u)", IPSET_MAXNAMELEN - 1);
212         ret = get_set_byname(args->data, &set_info);
213         if (ret < 0)
214                 return PARSE_ERR(args, "ipset: unknown set name '%s'", args->data);
215
216         if (args->next == NULL)
217                 return PARSE_ERR(args, "ipset: missing set flags");
218
219         args = bstr_next(args);
220         if (parse_dirs(args->data, &set_info))
221                 return PARSE_ERR(args, "ipset: error parsing set flags");
222
223         if (args->next) {
224                 args = bstr_next(args);
225                 return PARSE_ERR(args, "ipset: unknown parameter");
226         }
227
228         addraw_l(n, MAX_MSG, hdr, sizeof(*hdr));
229         addraw_l(n, MAX_MSG, &set_info, sizeof(set_info));
230
231 #undef PARSE_ERR
232         return 0;
233 }
234
235 static int ipset_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data,
236                             int data_len)
237 {
238         int i;
239         char setname[IPSET_MAXNAMELEN];
240         const struct xt_set_info *set_info = data;
241
242         if (data_len != sizeof(*set_info)) {
243                 fprintf(stderr, "xt_set_info struct size mismatch\n");
244                 return -1;
245         }
246
247         if (get_set_byid(setname, set_info->index))
248                 return -1;
249         fputs(setname, fd);
250         for (i = 1; i <= set_info->dim; i++) {
251                 fprintf(fd, "%s%s", i == 1 ? " " : ",", set_info->flags & (1 << i) ? "src" : "dst");
252         }
253
254         return 0;
255 }
256
257 struct ematch_util ipset_ematch_util = {
258         .kind = "ipset",
259         .kind_num = TCF_EM_IPSET,
260         .parse_eopt = ipset_parse_eopt,
261         .print_eopt = ipset_print_eopt,
262         .print_usage = ipset_print_usage
263 };