1 /* ipset.c is Copyright (c) 2013 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved.
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 dated June, 1991, or
6 (at your option) version 3 dated 29 June, 2007.
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
13 You should have received a copy of the GNU General Public License
14 along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <sys/utsname.h>
26 #include <arpa/inet.h>
27 #include <linux/version.h>
28 #include <linux/netlink.h>
29 #include <linux/netfilter/nfnetlink.h>
30 #ifndef NFNL_SUBSYS_IPSET
31 #define NFNL_SUBSYS_IPSET 6
32 #define IPSET_ATTR_DATA 7
33 #define IPSET_ATTR_IP 1
34 #define IPSET_ATTR_IPADDR_IPV4 1
35 #define IPSET_ATTR_IPADDR_IPV6 2
36 #define IPSET_ATTR_PROTOCOL 1
37 #define IPSET_ATTR_SETNAME 2
38 #define IPSET_CMD_ADD 9
39 #define IPSET_CMD_DEL 10
40 #define IPSET_MAXNAMELEN 32
41 #define IPSET_PROTOCOL 6
43 #include <linux/netfilter/ipset/ip_set.h>
46 /* data structure size in here is fixed */
49 #define NL_ALIGN(len) (((len)+3) & ~(3))
50 static const struct sockaddr_nl snl
= { .nl_family
= AF_NETLINK
};
51 static int ipset_sock
, old_kernel
;
54 static inline void add_attr(struct nlmsghdr
*nlh
, uint16_t type
, size_t len
, const void *data
)
56 struct nlattr
*attr
= (void *)nlh
+ NL_ALIGN(nlh
->nlmsg_len
);
57 uint16_t payload_len
= NL_ALIGN(sizeof(struct nlattr
)) + len
;
58 attr
->nla_type
= type
;
59 attr
->nla_len
= payload_len
;
60 memcpy((void *)attr
+ NL_ALIGN(sizeof(struct nlattr
)), data
, len
);
61 nlh
->nlmsg_len
+= NL_ALIGN(payload_len
);
66 struct utsname utsname
;
70 if (uname(&utsname
) < 0)
71 die(_("failed to find kernel version: %s"), NULL
, EC_MISC
);
73 split
= strtok(utsname
.release
, ".");
74 version
= (split
? atoi(split
) : 0);
75 split
= strtok(NULL
, ".");
76 version
= version
* 256 + (split
? atoi(split
) : 0);
77 split
= strtok(NULL
, ".");
78 version
= version
* 256 + (split
? atoi(split
) : 0);
79 old_kernel
= (version
< KERNEL_VERSION(2,6,32));
81 if (old_kernel
&& (ipset_sock
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
)) != -1)
85 (buffer
= safe_malloc(BUFF_SZ
)) &&
86 (ipset_sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_NETFILTER
)) != -1 &&
87 (bind(ipset_sock
, (struct sockaddr
*)&snl
, sizeof(snl
)) != -1))
90 die (_("failed to create IPset control socket: %s"), NULL
, EC_MISC
);
93 static int new_add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int af
, int remove
)
97 struct nlattr
*nested
[2];
99 int addrsz
= INADDRSZ
;
107 if (strlen(setname
) >= IPSET_MAXNAMELEN
)
109 errno
= ENAMETOOLONG
;
113 memset(buffer
, 0, BUFF_SZ
);
115 nlh
= (struct nlmsghdr
*)buffer
;
116 nlh
->nlmsg_len
= NL_ALIGN(sizeof(struct nlmsghdr
));
117 nlh
->nlmsg_type
= (remove
? IPSET_CMD_DEL
: IPSET_CMD_ADD
) | (NFNL_SUBSYS_IPSET
<< 8);
118 nlh
->nlmsg_flags
= NLM_F_REQUEST
;
120 nfg
= (struct nfgenmsg
*)(buffer
+ nlh
->nlmsg_len
);
121 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct nfgenmsg
));
122 nfg
->nfgen_family
= af
;
123 nfg
->version
= NFNETLINK_V0
;
124 nfg
->res_id
= htons(0);
126 proto
= IPSET_PROTOCOL
;
127 add_attr(nlh
, IPSET_ATTR_PROTOCOL
, sizeof(proto
), &proto
);
128 add_attr(nlh
, IPSET_ATTR_SETNAME
, strlen(setname
) + 1, setname
);
129 nested
[0] = (struct nlattr
*)(buffer
+ NL_ALIGN(nlh
->nlmsg_len
));
130 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct nlattr
));
131 nested
[0]->nla_type
= NLA_F_NESTED
| IPSET_ATTR_DATA
;
132 nested
[1] = (struct nlattr
*)(buffer
+ NL_ALIGN(nlh
->nlmsg_len
));
133 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct nlattr
));
134 nested
[1]->nla_type
= NLA_F_NESTED
| IPSET_ATTR_IP
;
136 (af
== AF_INET
? IPSET_ATTR_IPADDR_IPV4
: IPSET_ATTR_IPADDR_IPV6
) | NLA_F_NET_BYTEORDER
,
137 addrsz
, &ipaddr
->addr
);
138 nested
[1]->nla_len
= (void *)buffer
+ NL_ALIGN(nlh
->nlmsg_len
) - (void *)nested
[1];
139 nested
[0]->nla_len
= (void *)buffer
+ NL_ALIGN(nlh
->nlmsg_len
) - (void *)nested
[0];
141 while ((rc
= sendto(ipset_sock
, buffer
, nlh
->nlmsg_len
, 0,
142 (struct sockaddr
*)&snl
, sizeof(snl
))) == -1 && retry_send());
147 static int old_add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int remove
)
150 struct ip_set_req_adt_get
{
154 char name
[IPSET_MAXNAMELEN
];
157 char typename
[IPSET_MAXNAMELEN
];
159 struct ip_set_req_adt
{
165 if (strlen(setname
) >= sizeof(req_adt_get
.set
.name
))
167 errno
= ENAMETOOLONG
;
171 req_adt_get
.op
= 0x10;
172 req_adt_get
.version
= 3;
173 strcpy(req_adt_get
.set
.name
, setname
);
174 size
= sizeof(req_adt_get
);
175 if (getsockopt(ipset_sock
, SOL_IP
, 83, &req_adt_get
, &size
) < 0)
177 req_adt
.op
= remove
? 0x102 : 0x101;
178 req_adt
.index
= req_adt_get
.set
.index
;
179 req_adt
.ip
= ntohl(ipaddr
->addr
.addr4
.s_addr
);
180 if (setsockopt(ipset_sock
, SOL_IP
, 83, &req_adt
, sizeof(req_adt
)) < 0)
188 int add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int flags
, int remove
)
196 /* old method only supports IPv4 */
202 return old_kernel
? old_add_to_ipset(setname
, ipaddr
, remove
) : new_add_to_ipset(setname
, ipaddr
, af
, remove
);