1 /*
2 * (C) 2012-2014 by Pablo Neira Ayuso <[email protected]>
3 * (C) 2013 by Tomasz Bursztyka <[email protected]>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
11 */
12
13 #include <string.h>
14 #include <stdio.h>
15
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netinet/ip.h>
20 #include <netdb.h>
21
22 #include <xtables.h>
23
24 #include <linux/netfilter/nf_tables.h>
25
26 #include "nft.h"
27 #include "nft-shared.h"
28
nft_ipv4_add(struct nft_handle * h,struct nft_rule_ctx * ctx,struct nftnl_rule * r,struct iptables_command_state * cs)29 static int nft_ipv4_add(struct nft_handle *h, struct nft_rule_ctx *ctx,
30 struct nftnl_rule *r, struct iptables_command_state *cs)
31 {
32 struct xtables_rule_match *matchp;
33 uint32_t op;
34 int ret;
35
36 if (cs->fw.ip.src.s_addr || cs->fw.ip.smsk.s_addr || cs->fw.ip.invflags & IPT_INV_SRCIP) {
37 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_SRCIP);
38 add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER,
39 offsetof(struct iphdr, saddr),
40 &cs->fw.ip.src.s_addr, &cs->fw.ip.smsk.s_addr,
41 sizeof(struct in_addr), op);
42 }
43
44 if (cs->fw.ip.dst.s_addr || cs->fw.ip.dmsk.s_addr || cs->fw.ip.invflags & IPT_INV_DSTIP) {
45 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_DSTIP);
46 add_addr(h, r, NFT_PAYLOAD_NETWORK_HEADER,
47 offsetof(struct iphdr, daddr),
48 &cs->fw.ip.dst.s_addr, &cs->fw.ip.dmsk.s_addr,
49 sizeof(struct in_addr), op);
50 }
51
52 if (cs->fw.ip.iniface[0] != '\0') {
53 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_IN);
54 add_iface(h, r, cs->fw.ip.iniface, NFT_META_IIFNAME, op);
55 }
56
57 if (cs->fw.ip.outiface[0] != '\0') {
58 op = nft_invflags2cmp(cs->fw.ip.invflags, IPT_INV_VIA_OUT);
59 add_iface(h, r, cs->fw.ip.outiface, NFT_META_OIFNAME, op);
60 }
61
62 if (cs->fw.ip.proto != 0) {
63 op = nft_invflags2cmp(cs->fw.ip.invflags, XT_INV_PROTO);
64 add_proto(h, r, offsetof(struct iphdr, protocol),
65 sizeof(uint8_t), cs->fw.ip.proto, op);
66 }
67
68 if (cs->fw.ip.flags & IPT_F_FRAG) {
69 uint8_t reg;
70
71 add_payload(h, r, offsetof(struct iphdr, frag_off), 2,
72 NFT_PAYLOAD_NETWORK_HEADER, ®);
73 /* get the 13 bits that contain the fragment offset */
74 add_bitwise_u16(h, r, htons(0x1fff), 0, reg, ®);
75
76 /* if offset is non-zero, this is a fragment */
77 op = NFT_CMP_NEQ;
78 if (cs->fw.ip.invflags & IPT_INV_FRAG)
79 op = NFT_CMP_EQ;
80
81 add_cmp_u16(r, 0, op, reg);
82 }
83
84 add_compat(r, cs->fw.ip.proto, cs->fw.ip.invflags & XT_INV_PROTO);
85
86 for (matchp = cs->matches; matchp; matchp = matchp->next) {
87 ret = add_match(h, ctx, r, matchp->match->m);
88 if (ret < 0)
89 return ret;
90 }
91
92 /* Counters need to me added before the target, otherwise they are
93 * increased for each rule because of the way nf_tables works.
94 */
95 if (add_counters(r, cs->counters.pcnt, cs->counters.bcnt) < 0)
96 return -1;
97
98 return add_action(r, cs, !!(cs->fw.ip.flags & IPT_F_GOTO));
99 }
100
nft_ipv4_is_same(const struct iptables_command_state * a,const struct iptables_command_state * b)101 static bool nft_ipv4_is_same(const struct iptables_command_state *a,
102 const struct iptables_command_state *b)
103 {
104 if (a->fw.ip.src.s_addr != b->fw.ip.src.s_addr
105 || a->fw.ip.dst.s_addr != b->fw.ip.dst.s_addr
106 || a->fw.ip.smsk.s_addr != b->fw.ip.smsk.s_addr
107 || a->fw.ip.dmsk.s_addr != b->fw.ip.dmsk.s_addr
108 || a->fw.ip.proto != b->fw.ip.proto
109 || a->fw.ip.flags != b->fw.ip.flags
110 || a->fw.ip.invflags != b->fw.ip.invflags) {
111 DEBUGP("different src/dst/proto/flags/invflags\n");
112 return false;
113 }
114
115 return is_same_interfaces(a->fw.ip.iniface, a->fw.ip.outiface,
116 a->fw.ip.iniface_mask, a->fw.ip.outiface_mask,
117 b->fw.ip.iniface, b->fw.ip.outiface,
118 b->fw.ip.iniface_mask, b->fw.ip.outiface_mask);
119 }
120
nft_ipv4_set_goto_flag(struct iptables_command_state * cs)121 static void nft_ipv4_set_goto_flag(struct iptables_command_state *cs)
122 {
123 cs->fw.ip.flags |= IPT_F_GOTO;
124 }
125
nft_ipv4_print_rule(struct nft_handle * h,struct nftnl_rule * r,unsigned int num,unsigned int format)126 static void nft_ipv4_print_rule(struct nft_handle *h, struct nftnl_rule *r,
127 unsigned int num, unsigned int format)
128 {
129 struct iptables_command_state cs = {};
130
131 nft_rule_to_iptables_command_state(h, r, &cs);
132
133 print_rule_details(num, &cs.counters, cs.jumpto, cs.fw.ip.proto,
134 cs.fw.ip.flags, cs.fw.ip.invflags, format);
135 print_fragment(cs.fw.ip.flags, cs.fw.ip.invflags, format, false);
136 print_ifaces(cs.fw.ip.iniface, cs.fw.ip.outiface, cs.fw.ip.invflags,
137 format);
138 print_ipv4_addresses(&cs.fw, format);
139
140 if (format & FMT_NOTABLE)
141 fputs(" ", stdout);
142
143 #ifdef IPT_F_GOTO
144 if (cs.fw.ip.flags & IPT_F_GOTO)
145 printf("[goto] ");
146 #endif
147
148 print_matches_and_target(&cs, format);
149
150 if (!(format & FMT_NONEWLINE))
151 fputc('\n', stdout);
152
153 xtables_clear_iptables_command_state(&cs);
154 }
155
nft_ipv4_save_rule(const struct iptables_command_state * cs,unsigned int format)156 static void nft_ipv4_save_rule(const struct iptables_command_state *cs,
157 unsigned int format)
158 {
159 save_ipv4_addr('s', &cs->fw.ip.src, &cs->fw.ip.smsk,
160 cs->fw.ip.invflags & IPT_INV_SRCIP);
161 save_ipv4_addr('d', &cs->fw.ip.dst, &cs->fw.ip.dmsk,
162 cs->fw.ip.invflags & IPT_INV_DSTIP);
163
164 save_rule_details(cs->fw.ip.iniface, cs->fw.ip.iniface_mask,
165 cs->fw.ip.outiface, cs->fw.ip.outiface_mask,
166 cs->fw.ip.proto, cs->fw.ip.flags & IPT_F_FRAG,
167 cs->fw.ip.invflags);
168
169 save_matches_and_target(cs, cs->fw.ip.flags & IPT_F_GOTO,
170 &cs->fw, format);
171 }
172
xlate_ipv4_addr(const char * selector,const struct in_addr * addr,const struct in_addr * mask,bool inv,struct xt_xlate * xl)173 static void xlate_ipv4_addr(const char *selector, const struct in_addr *addr,
174 const struct in_addr *mask,
175 bool inv, struct xt_xlate *xl)
176 {
177 char mbuf[INET_ADDRSTRLEN], abuf[INET_ADDRSTRLEN];
178 const char *op = inv ? "!= " : "";
179 int cidr;
180
181 if (!inv && !addr->s_addr && !mask->s_addr)
182 return;
183
184 inet_ntop(AF_INET, addr, abuf, sizeof(abuf));
185
186 cidr = xtables_ipmask_to_cidr(mask);
187 switch (cidr) {
188 case -1:
189 xt_xlate_add(xl, "%s & %s %s %s ", selector,
190 inet_ntop(AF_INET, mask, mbuf, sizeof(mbuf)),
191 inv ? "!=" : "==", abuf);
192 break;
193 case 32:
194 xt_xlate_add(xl, "%s %s%s ", selector, op, abuf);
195 break;
196 default:
197 xt_xlate_add(xl, "%s %s%s/%d ", selector, op, abuf, cidr);
198 }
199 }
200
nft_ipv4_xlate(const struct iptables_command_state * cs,struct xt_xlate * xl)201 static int nft_ipv4_xlate(const struct iptables_command_state *cs,
202 struct xt_xlate *xl)
203 {
204 const char *comment;
205 int ret;
206
207 xlate_ifname(xl, "iifname", cs->fw.ip.iniface,
208 cs->fw.ip.invflags & IPT_INV_VIA_IN);
209 xlate_ifname(xl, "oifname", cs->fw.ip.outiface,
210 cs->fw.ip.invflags & IPT_INV_VIA_OUT);
211
212 if (cs->fw.ip.flags & IPT_F_FRAG) {
213 xt_xlate_add(xl, "ip frag-off & 0x1fff %s%x ",
214 cs->fw.ip.invflags & IPT_INV_FRAG? "" : "!= ", 0);
215 }
216
217 if (cs->fw.ip.proto != 0) {
218 const struct protoent *pent =
219 getprotobynumber(cs->fw.ip.proto);
220 char protonum[sizeof("65535")];
221 const char *name = protonum;
222
223 snprintf(protonum, sizeof(protonum), "%u",
224 cs->fw.ip.proto);
225
226 if (!pent || !xlate_find_match(cs, pent->p_name)) {
227 if (pent)
228 name = pent->p_name;
229 xt_xlate_add(xl, "ip protocol %s%s ",
230 cs->fw.ip.invflags & IPT_INV_PROTO ?
231 "!= " : "", name);
232 }
233 }
234
235 xlate_ipv4_addr("ip saddr", &cs->fw.ip.src, &cs->fw.ip.smsk,
236 cs->fw.ip.invflags & IPT_INV_SRCIP, xl);
237 xlate_ipv4_addr("ip daddr", &cs->fw.ip.dst, &cs->fw.ip.dmsk,
238 cs->fw.ip.invflags & IPT_INV_DSTIP, xl);
239
240 ret = xlate_matches(cs, xl);
241 if (!ret)
242 return ret;
243
244 /* Always add counters per rule, as in iptables */
245 xt_xlate_add(xl, "counter");
246 ret = xlate_action(cs, !!(cs->fw.ip.flags & IPT_F_GOTO), xl);
247
248 comment = xt_xlate_get_comment(xl);
249 if (comment)
250 xt_xlate_add(xl, " comment %s", comment);
251
252 return ret;
253 }
254
255 static int
nft_ipv4_add_entry(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,struct xtables_args * args,bool verbose,bool append,int rulenum)256 nft_ipv4_add_entry(struct nft_handle *h,
257 const char *chain, const char *table,
258 struct iptables_command_state *cs,
259 struct xtables_args *args, bool verbose,
260 bool append, int rulenum)
261 {
262 unsigned int i, j;
263 int ret = 1;
264
265 for (i = 0; i < args->s.naddrs; i++) {
266 cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
267 cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
268 for (j = 0; j < args->d.naddrs; j++) {
269 cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr;
270 cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr;
271
272 if (append) {
273 ret = nft_cmd_rule_append(h, chain, table,
274 cs, verbose);
275 } else {
276 ret = nft_cmd_rule_insert(h, chain, table,
277 cs, rulenum, verbose);
278 }
279 }
280 }
281
282 return ret;
283 }
284
285 static int
nft_ipv4_delete_entry(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,struct xtables_args * args,bool verbose)286 nft_ipv4_delete_entry(struct nft_handle *h,
287 const char *chain, const char *table,
288 struct iptables_command_state *cs,
289 struct xtables_args *args, bool verbose)
290 {
291 unsigned int i, j;
292 int ret = 1;
293
294 for (i = 0; i < args->s.naddrs; i++) {
295 cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
296 cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
297 for (j = 0; j < args->d.naddrs; j++) {
298 cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr;
299 cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr;
300 ret = nft_cmd_rule_delete(h, chain, table, cs, verbose);
301 }
302 }
303
304 return ret;
305 }
306
307 static int
nft_ipv4_check_entry(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,struct xtables_args * args,bool verbose)308 nft_ipv4_check_entry(struct nft_handle *h,
309 const char *chain, const char *table,
310 struct iptables_command_state *cs,
311 struct xtables_args *args, bool verbose)
312 {
313 unsigned int i, j;
314 int ret = 1;
315
316 for (i = 0; i < args->s.naddrs; i++) {
317 cs->fw.ip.src.s_addr = args->s.addr.v4[i].s_addr;
318 cs->fw.ip.smsk.s_addr = args->s.mask.v4[i].s_addr;
319 for (j = 0; j < args->d.naddrs; j++) {
320 cs->fw.ip.dst.s_addr = args->d.addr.v4[j].s_addr;
321 cs->fw.ip.dmsk.s_addr = args->d.mask.v4[j].s_addr;
322 ret = nft_cmd_rule_check(h, chain, table, cs, verbose);
323 }
324 }
325
326 return ret;
327 }
328
329 static int
nft_ipv4_replace_entry(struct nft_handle * h,const char * chain,const char * table,struct iptables_command_state * cs,struct xtables_args * args,bool verbose,int rulenum)330 nft_ipv4_replace_entry(struct nft_handle *h,
331 const char *chain, const char *table,
332 struct iptables_command_state *cs,
333 struct xtables_args *args, bool verbose,
334 int rulenum)
335 {
336 cs->fw.ip.src.s_addr = args->s.addr.v4->s_addr;
337 cs->fw.ip.dst.s_addr = args->d.addr.v4->s_addr;
338 cs->fw.ip.smsk.s_addr = args->s.mask.v4->s_addr;
339 cs->fw.ip.dmsk.s_addr = args->d.mask.v4->s_addr;
340
341 return nft_cmd_rule_replace(h, chain, table, cs, rulenum, verbose);
342 }
343
344 struct nft_family_ops nft_family_ops_ipv4 = {
345 .add = nft_ipv4_add,
346 .is_same = nft_ipv4_is_same,
347 .set_goto_flag = nft_ipv4_set_goto_flag,
348 .print_header = print_header,
349 .print_rule = nft_ipv4_print_rule,
350 .save_rule = nft_ipv4_save_rule,
351 .save_chain = nft_ipv46_save_chain,
352 .rule_parse = &nft_ruleparse_ops_ipv4,
353 .cmd_parse = {
354 .proto_parse = ipv4_proto_parse,
355 .post_parse = ipv4_post_parse,
356 },
357 .rule_to_cs = nft_rule_to_iptables_command_state,
358 .clear_cs = xtables_clear_iptables_command_state,
359 .xlate = nft_ipv4_xlate,
360 .add_entry = nft_ipv4_add_entry,
361 .delete_entry = nft_ipv4_delete_entry,
362 .check_entry = nft_ipv4_check_entry,
363 .replace_entry = nft_ipv4_replace_entry,
364 };
365