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>
30 /* We want to be able to compile against old header files
31 Kernel version is handled at run-time. */
33 #define NFNL_SUBSYS_IPSET 6
35 #define IPSET_ATTR_DATA 7
36 #define IPSET_ATTR_IP 1
37 #define IPSET_ATTR_IPADDR_IPV4 1
38 #define IPSET_ATTR_IPADDR_IPV6 2
39 #define IPSET_ATTR_PROTOCOL 1
40 #define IPSET_ATTR_SETNAME 2
41 #define IPSET_CMD_ADD 9
42 #define IPSET_CMD_DEL 10
43 #define IPSET_MAXNAMELEN 32
44 #define IPSET_PROTOCOL 6
47 #define NFNETLINK_V0 0
51 #define NLA_F_NESTED (1 << 15)
54 #ifndef NLA_F_NET_BYTEORDER
55 #define NLA_F_NET_BYTEORDER (1 << 14)
64 __u8 nfgen_family
; /* AF_xxx */
65 __u8 version
; /* nfnetlink version */
66 __be16 res_id
; /* resource id */
70 /* data structure size in here is fixed */
73 #define NL_ALIGN(len) (((len)+3) & ~(3))
74 static const struct sockaddr_nl snl
= { .nl_family
= AF_NETLINK
};
75 static int ipset_sock
, old_kernel
;
78 static inline void add_attr(struct nlmsghdr
*nlh
, uint16_t type
, size_t len
, const void *data
)
80 struct my_nlattr
*attr
= (void *)nlh
+ NL_ALIGN(nlh
->nlmsg_len
);
81 uint16_t payload_len
= NL_ALIGN(sizeof(struct my_nlattr
)) + len
;
82 attr
->nla_type
= type
;
83 attr
->nla_len
= payload_len
;
84 memcpy((void *)attr
+ NL_ALIGN(sizeof(struct my_nlattr
)), data
, len
);
85 nlh
->nlmsg_len
+= NL_ALIGN(payload_len
);
90 struct utsname utsname
;
94 if (uname(&utsname
) < 0)
95 die(_("failed to find kernel version: %s"), NULL
, EC_MISC
);
97 split
= strtok(utsname
.release
, ".");
98 version
= (split
? atoi(split
) : 0);
99 split
= strtok(NULL
, ".");
100 version
= version
* 256 + (split
? atoi(split
) : 0);
101 split
= strtok(NULL
, ".");
102 version
= version
* 256 + (split
? atoi(split
) : 0);
103 old_kernel
= (version
< KERNEL_VERSION(2,6,32));
105 if (old_kernel
&& (ipset_sock
= socket(AF_INET
, SOCK_RAW
, IPPROTO_RAW
)) != -1)
109 (buffer
= safe_malloc(BUFF_SZ
)) &&
110 (ipset_sock
= socket(AF_NETLINK
, SOCK_RAW
, NETLINK_NETFILTER
)) != -1 &&
111 (bind(ipset_sock
, (struct sockaddr
*)&snl
, sizeof(snl
)) != -1))
114 die (_("failed to create IPset control socket: %s"), NULL
, EC_MISC
);
117 static int new_add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int af
, int remove
)
119 struct nlmsghdr
*nlh
;
120 struct my_nfgenmsg
*nfg
;
121 struct my_nlattr
*nested
[2];
123 int addrsz
= INADDRSZ
;
131 if (strlen(setname
) >= IPSET_MAXNAMELEN
)
133 errno
= ENAMETOOLONG
;
137 memset(buffer
, 0, BUFF_SZ
);
139 nlh
= (struct nlmsghdr
*)buffer
;
140 nlh
->nlmsg_len
= NL_ALIGN(sizeof(struct nlmsghdr
));
141 nlh
->nlmsg_type
= (remove
? IPSET_CMD_DEL
: IPSET_CMD_ADD
) | (NFNL_SUBSYS_IPSET
<< 8);
142 nlh
->nlmsg_flags
= NLM_F_REQUEST
;
144 nfg
= (struct my_nfgenmsg
*)(buffer
+ nlh
->nlmsg_len
);
145 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct my_nfgenmsg
));
146 nfg
->nfgen_family
= af
;
147 nfg
->version
= NFNETLINK_V0
;
148 nfg
->res_id
= htons(0);
150 proto
= IPSET_PROTOCOL
;
151 add_attr(nlh
, IPSET_ATTR_PROTOCOL
, sizeof(proto
), &proto
);
152 add_attr(nlh
, IPSET_ATTR_SETNAME
, strlen(setname
) + 1, setname
);
153 nested
[0] = (struct my_nlattr
*)(buffer
+ NL_ALIGN(nlh
->nlmsg_len
));
154 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct my_nlattr
));
155 nested
[0]->nla_type
= NLA_F_NESTED
| IPSET_ATTR_DATA
;
156 nested
[1] = (struct my_nlattr
*)(buffer
+ NL_ALIGN(nlh
->nlmsg_len
));
157 nlh
->nlmsg_len
+= NL_ALIGN(sizeof(struct my_nlattr
));
158 nested
[1]->nla_type
= NLA_F_NESTED
| IPSET_ATTR_IP
;
160 (af
== AF_INET
? IPSET_ATTR_IPADDR_IPV4
: IPSET_ATTR_IPADDR_IPV6
) | NLA_F_NET_BYTEORDER
,
161 addrsz
, &ipaddr
->addr
);
162 nested
[1]->nla_len
= (void *)buffer
+ NL_ALIGN(nlh
->nlmsg_len
) - (void *)nested
[1];
163 nested
[0]->nla_len
= (void *)buffer
+ NL_ALIGN(nlh
->nlmsg_len
) - (void *)nested
[0];
165 while ((rc
= sendto(ipset_sock
, buffer
, nlh
->nlmsg_len
, 0,
166 (struct sockaddr
*)&snl
, sizeof(snl
))) == -1 && retry_send());
171 static int old_add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int remove
)
174 struct ip_set_req_adt_get
{
178 char name
[IPSET_MAXNAMELEN
];
181 char typename
[IPSET_MAXNAMELEN
];
183 struct ip_set_req_adt
{
189 if (strlen(setname
) >= sizeof(req_adt_get
.set
.name
))
191 errno
= ENAMETOOLONG
;
195 req_adt_get
.op
= 0x10;
196 req_adt_get
.version
= 3;
197 strcpy(req_adt_get
.set
.name
, setname
);
198 size
= sizeof(req_adt_get
);
199 if (getsockopt(ipset_sock
, SOL_IP
, 83, &req_adt_get
, &size
) < 0)
201 req_adt
.op
= remove
? 0x102 : 0x101;
202 req_adt
.index
= req_adt_get
.set
.index
;
203 req_adt
.ip
= ntohl(ipaddr
->addr
.addr4
.s_addr
);
204 if (setsockopt(ipset_sock
, SOL_IP
, 83, &req_adt
, sizeof(req_adt
)) < 0)
212 int add_to_ipset(const char *setname
, const struct all_addr
*ipaddr
, int flags
, int remove
)
220 /* old method only supports IPv4 */
226 return old_kernel
? old_add_to_ipset(setname
, ipaddr
, remove
) : new_add_to_ipset(setname
, ipaddr
, af
, remove
);