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