1 #ifndef _LIBXT_SET_H
2 #define _LIBXT_SET_H
3
4 #include <unistd.h>
5 #include <fcntl.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <errno.h>
9 #include "../iptables/xshared.h"
10
11 #ifdef DEBUG
12 #define DEBUGP(x, args...) fprintf(stderr, x , ## args)
13 #else
14 #define DEBUGP(x, args...)
15 #endif
16
17 static int
get_version(unsigned * version)18 get_version(unsigned *version)
19 {
20 int res, sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
21 struct ip_set_req_version req_version;
22 socklen_t size = sizeof(req_version);
23
24 if (sockfd < 0)
25 xtables_error(OTHER_PROBLEM,
26 "Can't open socket to ipset.\n");
27
28 if (fcntl(sockfd, F_SETFD, FD_CLOEXEC) == -1) {
29 xtables_error(OTHER_PROBLEM,
30 "Could not set close on exec: %s\n",
31 strerror(errno));
32 }
33
34 req_version.op = IP_SET_OP_VERSION;
35 res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req_version, &size);
36 if (res != 0)
37 xtables_error(OTHER_PROBLEM,
38 "Kernel module xt_set is not loaded in.\n");
39
40 *version = req_version.version;
41
42 return sockfd;
43 }
44
45 static void
get_set_byid(char * setname,ip_set_id_t idx)46 get_set_byid(char *setname, ip_set_id_t idx)
47 {
48 struct ip_set_req_get_set req;
49 socklen_t size = sizeof(struct ip_set_req_get_set);
50 int res, sockfd;
51
52 sockfd = get_version(&req.version);
53 req.op = IP_SET_OP_GET_BYINDEX;
54 req.set.index = idx;
55 res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
56 close(sockfd);
57
58 if (res != 0)
59 xtables_error(OTHER_PROBLEM,
60 "Problem when communicating with ipset, errno=%d.\n",
61 errno);
62 if (size != sizeof(struct ip_set_req_get_set))
63 xtables_error(OTHER_PROBLEM,
64 "Incorrect return size from kernel during ipset lookup, "
65 "(want %zu, got %zu)\n",
66 sizeof(struct ip_set_req_get_set), (size_t)size);
67 if (req.set.name[0] == '\0')
68 xtables_error(PARAMETER_PROBLEM,
69 "Set with index %i in kernel doesn't exist.\n", idx);
70
71 strncpy(setname, req.set.name, IPSET_MAXNAMELEN);
72 }
73
74 static void
get_set_byname_only(const char * setname,struct xt_set_info * info,int sockfd,unsigned int version)75 get_set_byname_only(const char *setname, struct xt_set_info *info,
76 int sockfd, unsigned int version)
77 {
78 struct ip_set_req_get_set req = { .version = version };
79 socklen_t size = sizeof(struct ip_set_req_get_set);
80 int res;
81
82 req.op = IP_SET_OP_GET_BYNAME;
83 strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
84 req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
85 res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
86 close(sockfd);
87
88 if (res != 0)
89 xtables_error(OTHER_PROBLEM,
90 "Problem when communicating with ipset, errno=%d.\n",
91 errno);
92 if (size != sizeof(struct ip_set_req_get_set))
93 xtables_error(OTHER_PROBLEM,
94 "Incorrect return size from kernel during ipset lookup, "
95 "(want %zu, got %zu)\n",
96 sizeof(struct ip_set_req_get_set), (size_t)size);
97 if (req.set.index == IPSET_INVALID_ID)
98 xtables_error(PARAMETER_PROBLEM,
99 "Set %s doesn't exist.\n", setname);
100
101 info->index = req.set.index;
102 }
103
104 static void
get_set_byname(const char * setname,struct xt_set_info * info)105 get_set_byname(const char *setname, struct xt_set_info *info)
106 {
107 struct ip_set_req_get_set_family req;
108 socklen_t size = sizeof(struct ip_set_req_get_set_family);
109 int res, sockfd, version;
110
111 sockfd = get_version(&req.version);
112 version = req.version;
113 req.op = IP_SET_OP_GET_FNAME;
114 strncpy(req.set.name, setname, IPSET_MAXNAMELEN);
115 req.set.name[IPSET_MAXNAMELEN - 1] = '\0';
116 res = getsockopt(sockfd, SOL_IP, SO_IP_SET, &req, &size);
117
118 if (res != 0 && errno == EBADMSG)
119 /* Backward compatibility */
120 return get_set_byname_only(setname, info, sockfd, version);
121
122 close(sockfd);
123 if (res != 0)
124 xtables_error(OTHER_PROBLEM,
125 "Problem when communicating with ipset, errno=%d.\n",
126 errno);
127 if (size != sizeof(struct ip_set_req_get_set_family))
128 xtables_error(OTHER_PROBLEM,
129 "Incorrect return size from kernel during ipset lookup, "
130 "(want %zu, got %zu)\n",
131 sizeof(struct ip_set_req_get_set_family),
132 (size_t)size);
133 if (req.set.index == IPSET_INVALID_ID)
134 xtables_error(PARAMETER_PROBLEM,
135 "Set %s doesn't exist.\n", setname);
136 if (!(req.family == afinfo->family ||
137 req.family == NFPROTO_UNSPEC))
138 xtables_error(PARAMETER_PROBLEM,
139 "The protocol family of set %s is %s, "
140 "which is not applicable.\n",
141 setname,
142 req.family == NFPROTO_IPV4 ? "IPv4" : "IPv6");
143
144 info->index = req.set.index;
145 }
146
147 static void
parse_dirs_v0(const char * opt_arg,struct xt_set_info_v0 * info)148 parse_dirs_v0(const char *opt_arg, struct xt_set_info_v0 *info)
149 {
150 char *saved = strdup(opt_arg);
151 char *ptr, *tmp = saved;
152 int i = 0;
153
154 while (i < (IPSET_DIM_MAX - 1) && tmp != NULL) {
155 ptr = strsep(&tmp, ",");
156 if (strncmp(ptr, "src", 3) == 0)
157 info->u.flags[i++] |= IPSET_SRC;
158 else if (strncmp(ptr, "dst", 3) == 0)
159 info->u.flags[i++] |= IPSET_DST;
160 else
161 xtables_error(PARAMETER_PROBLEM,
162 "You must spefify (the comma separated list of) 'src' or 'dst'.");
163 }
164
165 if (tmp)
166 xtables_error(PARAMETER_PROBLEM,
167 "Can't be more src/dst options than %i.",
168 IPSET_DIM_MAX);
169
170 free(saved);
171 }
172
173 static void
parse_dirs(const char * opt_arg,struct xt_set_info * info)174 parse_dirs(const char *opt_arg, struct xt_set_info *info)
175 {
176 char *saved = strdup(opt_arg);
177 char *ptr, *tmp = saved;
178
179 while (info->dim < IPSET_DIM_MAX && tmp != NULL) {
180 info->dim++;
181 ptr = strsep(&tmp, ",");
182 if (strncmp(ptr, "src", 3) == 0)
183 info->flags |= (1 << info->dim);
184 else if (strncmp(ptr, "dst", 3) != 0)
185 xtables_error(PARAMETER_PROBLEM,
186 "You must spefify (the comma separated list of) 'src' or 'dst'.");
187 }
188
189 if (tmp)
190 xtables_error(PARAMETER_PROBLEM,
191 "Can't be more src/dst options than %i.",
192 IPSET_DIM_MAX);
193
194 free(saved);
195 }
196
197 #endif /*_LIBXT_SET_H*/
198