Home | History | Annotate | Download | only in extensions
      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
     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
     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
     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
    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
    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
    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