iproute2/tc/em_ipset.c
<<
>>
Prefs
   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
  30typedef __u16 ip_set_id_t;
  31
  32enum 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
  48union 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 */
  54struct 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 */
  64struct ip_set_req_version {
  65        unsigned int op;
  66        unsigned int version;
  67};
  68#endif /* IPSET_INVALID_ID */
  69
  70extern struct ematch_util ipset_ematch_util;
  71
  72static 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
  95static 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
 120static int
 121get_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
 141static int
 142get_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
 158static int
 159parse_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
 187static 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
 198static 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
 235static 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
 257struct 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};
 264