1 /* 2 * em_ipset.c IPset Ematch 3 * 4 * (C) 2012 Florian Westphal <fw (at) strlen.de> 5 * 6 * Parts taken from iptables libxt_set.h: 7 * Copyright (C) 2000-2002 Joakim Axelsson <gozem (at) linux.nu> 8 * Patrick Schaaf <bof (at) bof.de> 9 * Martin Josefsson <gandalf (at) wlug.westbo.se> 10 * Copyright (C) 2003-2010 Jozsef Kadlecsik <kadlec (at) 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 op; 56 unsigned 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 op; 66 unsigned version; 67 }; 68 #endif /* IPSET_INVALID_ID */ 69 70 extern struct ematch_util ipset_ematch_util; 71 72 static int get_version(unsigned *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 return -1; 88 } 89 90 *version = req_version.version; 91 return sockfd; 92 } 93 94 static int do_getsockopt(struct ip_set_req_get_set *req) 95 { 96 int sockfd, res; 97 socklen_t size = sizeof(struct ip_set_req_get_set); 98 sockfd = get_version(&req->version); 99 if (sockfd < 0) 100 return -1; 101 res = getsockopt(sockfd, SOL_IP, SO_IP_SET, req, &size); 102 if (res != 0) 103 perror("Problem when communicating with ipset"); 104 close(sockfd); 105 if (res != 0) 106 return -1; 107 108 if (size != sizeof(struct ip_set_req_get_set)) { 109 fprintf(stderr, 110 "Incorrect return size from kernel during ipset lookup, " 111 "(want %zu, got %zu)\n", 112 sizeof(struct ip_set_req_get_set), (size_t)size); 113 return -1; 114 } 115 116 return res; 117 } 118 119 static int 120 get_set_byid(char *setname, unsigned int idx) 121 { 122 struct ip_set_req_get_set req; 123 int res; 124 125 req.op = IP_SET_OP_GET_BYINDEX; 126 req.set.index = idx; 127 res = do_getsockopt(&req); 128 if (res != 0) 129 return -1; 130 if (req.set.name[0] == '\0') { 131 fprintf(stderr, 132 "Set with index %i in kernel doesn't exist.\n", idx); 133 return -1; 134 } 135 136 strncpy(setname, req.set.name, IPSET_MAXNAMELEN); 137 return 0; 138 } 139 140 static int 141 get_set_byname(const char *setname, struct xt_set_info *info) 142 { 143 struct ip_set_req_get_set req; 144 int res; 145 146 req.op = IP_SET_OP_GET_BYNAME; 147 strncpy(req.set.name, setname, IPSET_MAXNAMELEN); 148 req.set.name[IPSET_MAXNAMELEN - 1] = '\0'; 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 memset(&set_info, 0, sizeof(set_info)); 205 206 #define PARSE_ERR(CARG, FMT, ARGS...) \ 207 em_parse_error(EINVAL, args, CARG, &ipset_ematch_util, FMT ,##ARGS) 208 209 if (args == NULL) 210 return PARSE_ERR(args, "ipset: missing set name"); 211 212 if (args->len >= IPSET_MAXNAMELEN) 213 return PARSE_ERR(args, "ipset: set name too long (max %u)", IPSET_MAXNAMELEN - 1); 214 ret = get_set_byname(args->data, &set_info); 215 if (ret < 0) 216 return PARSE_ERR(args, "ipset: unknown set name '%s'", args->data); 217 218 if (args->next == NULL) 219 return PARSE_ERR(args, "ipset: missing set flags"); 220 221 args = bstr_next(args); 222 if (parse_dirs(args->data, &set_info)) 223 return PARSE_ERR(args, "ipset: error parsing set flags"); 224 225 if (args->next) { 226 args = bstr_next(args); 227 return PARSE_ERR(args, "ipset: unknown parameter"); 228 } 229 230 addraw_l(n, MAX_MSG, hdr, sizeof(*hdr)); 231 addraw_l(n, MAX_MSG, &set_info, sizeof(set_info)); 232 233 #undef PARSE_ERR 234 return 0; 235 } 236 237 static int ipset_print_eopt(FILE *fd, struct tcf_ematch_hdr *hdr, void *data, 238 int data_len) 239 { 240 int i; 241 char setname[IPSET_MAXNAMELEN]; 242 const struct xt_set_info *set_info = data; 243 244 if (data_len != sizeof(*set_info)) { 245 fprintf(stderr, "xt_set_info struct size mismatch\n"); 246 return -1; 247 } 248 249 if (get_set_byid(setname, set_info->index)) 250 return -1; 251 fputs(setname, fd); 252 for (i = 1; i <= set_info->dim; i++) { 253 fprintf(fd, "%s%s", i == 1 ? " " : ",", set_info->flags & (1 << i) ? "src" : "dst"); 254 } 255 256 return 0; 257 } 258 259 struct ematch_util ipset_ematch_util = { 260 .kind = "ipset", 261 .kind_num = TCF_EM_IPSET, 262 .parse_eopt = ipset_parse_eopt, 263 .print_eopt = ipset_print_eopt, 264 .print_usage = ipset_print_usage 265 }; 266