2 * (C) 2012-2014 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2013 by Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
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.
10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <arpa/inet.h>
19 #include <netinet/ip.h>
24 #include <linux/netfilter/nf_tables.h>
27 #include "nft-shared.h"
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
)
32 struct xtables_rule_match
*matchp
;
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
);
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
);
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
);
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
);
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
);
68 if (cs
->fw
.ip
.flags
& IPT_F_FRAG
) {
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
, ®
);
76 /* if offset is non-zero, this is a fragment */
78 if (cs
->fw
.ip
.invflags
& IPT_INV_FRAG
)
81 add_cmp_u16(r
, 0, op
, reg
);
84 add_compat(r
, cs
->fw
.ip
.proto
, cs
->fw
.ip
.invflags
& XT_INV_PROTO
);
86 for (matchp
= cs
->matches
; matchp
; matchp
= matchp
->next
) {
87 ret
= add_match(h
, ctx
, r
, matchp
->match
->m
);
92 /* Counters need to me added before the target, otherwise they are
93 * increased for each rule because of the way nf_tables works.
95 if (add_counters(r
, cs
->counters
.pcnt
, cs
->counters
.bcnt
) < 0)
98 return add_action(r
, cs
, !!(cs
->fw
.ip
.flags
& IPT_F_GOTO
));
101 static bool nft_ipv4_is_same(const struct iptables_command_state
*a
,
102 const struct iptables_command_state
*b
)
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");
115 return is_same_interfaces(a
->fw
.ip
.iniface
, a
->fw
.ip
.outiface
,
116 b
->fw
.ip
.iniface
, b
->fw
.ip
.outiface
);
119 static void nft_ipv4_set_goto_flag(struct iptables_command_state
*cs
)
121 cs
->fw
.ip
.flags
|= IPT_F_GOTO
;
124 static void nft_ipv4_print_rule(struct nft_handle
*h
, struct nftnl_rule
*r
,
125 unsigned int num
, unsigned int format
)
127 struct iptables_command_state cs
= {};
129 nft_rule_to_iptables_command_state(h
, r
, &cs
);
131 print_rule_details(num
, &cs
.counters
, cs
.jumpto
, cs
.fw
.ip
.proto
,
132 cs
.fw
.ip
.flags
, cs
.fw
.ip
.invflags
, format
);
133 print_fragment(cs
.fw
.ip
.flags
, cs
.fw
.ip
.invflags
, format
, false);
134 print_ifaces(cs
.fw
.ip
.iniface
, cs
.fw
.ip
.outiface
, cs
.fw
.ip
.invflags
,
136 print_ipv4_addresses(&cs
.fw
, format
);
138 if (format
& FMT_NOTABLE
)
142 if (cs
.fw
.ip
.flags
& IPT_F_GOTO
)
146 print_matches_and_target(&cs
, format
);
148 if (!(format
& FMT_NONEWLINE
))
151 xtables_clear_iptables_command_state(&cs
);
154 static void nft_ipv4_save_rule(const struct iptables_command_state
*cs
,
157 save_ipv4_addr('s', &cs
->fw
.ip
.src
, &cs
->fw
.ip
.smsk
,
158 cs
->fw
.ip
.invflags
& IPT_INV_SRCIP
);
159 save_ipv4_addr('d', &cs
->fw
.ip
.dst
, &cs
->fw
.ip
.dmsk
,
160 cs
->fw
.ip
.invflags
& IPT_INV_DSTIP
);
162 save_rule_details(cs
->fw
.ip
.iniface
, cs
->fw
.ip
.outiface
,
163 cs
->fw
.ip
.proto
, cs
->fw
.ip
.flags
& IPT_F_FRAG
,
166 save_matches_and_target(cs
, cs
->fw
.ip
.flags
& IPT_F_GOTO
,
170 static void xlate_ipv4_addr(const char *selector
, const struct in_addr
*addr
,
171 const struct in_addr
*mask
,
172 bool inv
, struct xt_xlate
*xl
)
174 char mbuf
[INET_ADDRSTRLEN
], abuf
[INET_ADDRSTRLEN
];
175 const char *op
= inv
? "!= " : "";
178 if (!inv
&& !addr
->s_addr
&& !mask
->s_addr
)
181 inet_ntop(AF_INET
, addr
, abuf
, sizeof(abuf
));
183 cidr
= xtables_ipmask_to_cidr(mask
);
186 xt_xlate_add(xl
, "%s & %s %s %s ", selector
,
187 inet_ntop(AF_INET
, mask
, mbuf
, sizeof(mbuf
)),
188 inv
? "!=" : "==", abuf
);
191 xt_xlate_add(xl
, "%s %s%s ", selector
, op
, abuf
);
194 xt_xlate_add(xl
, "%s %s%s/%d ", selector
, op
, abuf
, cidr
);
198 static int nft_ipv4_xlate(const struct iptables_command_state
*cs
,
201 uint16_t proto
= cs
->fw
.ip
.proto
;
205 xlate_ifname(xl
, "iifname", cs
->fw
.ip
.iniface
,
206 cs
->fw
.ip
.invflags
& IPT_INV_VIA_IN
);
207 xlate_ifname(xl
, "oifname", cs
->fw
.ip
.outiface
,
208 cs
->fw
.ip
.invflags
& IPT_INV_VIA_OUT
);
210 if (cs
->fw
.ip
.flags
& IPT_F_FRAG
) {
211 xt_xlate_add(xl
, "ip frag-off & 0x1fff %s%x ",
212 cs
->fw
.ip
.invflags
& IPT_INV_FRAG
? "" : "!= ", 0);
215 if (proto
!= 0 && !xlate_find_protomatch(cs
, proto
)) {
216 const char *pname
= proto_to_name(proto
, 0);
218 xt_xlate_add(xl
, "ip protocol");
219 if (cs
->fw
.ip
.invflags
& IPT_INV_PROTO
)
220 xt_xlate_add(xl
, " !=");
222 xt_xlate_add(xl
, "%s", pname
);
224 xt_xlate_add(xl
, "%hu", proto
);
227 xlate_ipv4_addr("ip saddr", &cs
->fw
.ip
.src
, &cs
->fw
.ip
.smsk
,
228 cs
->fw
.ip
.invflags
& IPT_INV_SRCIP
, xl
);
229 xlate_ipv4_addr("ip daddr", &cs
->fw
.ip
.dst
, &cs
->fw
.ip
.dmsk
,
230 cs
->fw
.ip
.invflags
& IPT_INV_DSTIP
, xl
);
232 ret
= xlate_matches(cs
, xl
);
236 /* Always add counters per rule, as in iptables */
237 xt_xlate_add(xl
, "counter");
238 ret
= xlate_action(cs
, !!(cs
->fw
.ip
.flags
& IPT_F_GOTO
), xl
);
240 comment
= xt_xlate_get_comment(xl
);
242 xt_xlate_add(xl
, " comment %s", comment
);
248 nft_ipv4_add_entry(struct nft_handle
*h
,
249 const char *chain
, const char *table
,
250 struct iptables_command_state
*cs
,
251 struct xtables_args
*args
, bool verbose
,
252 bool append
, int rulenum
)
257 for (i
= 0; i
< args
->s
.naddrs
; i
++) {
258 cs
->fw
.ip
.src
.s_addr
= args
->s
.addr
.v4
[i
].s_addr
;
259 cs
->fw
.ip
.smsk
.s_addr
= args
->s
.mask
.v4
[i
].s_addr
;
260 for (j
= 0; j
< args
->d
.naddrs
; j
++) {
261 cs
->fw
.ip
.dst
.s_addr
= args
->d
.addr
.v4
[j
].s_addr
;
262 cs
->fw
.ip
.dmsk
.s_addr
= args
->d
.mask
.v4
[j
].s_addr
;
265 ret
= nft_cmd_rule_append(h
, chain
, table
,
268 ret
= nft_cmd_rule_insert(h
, chain
, table
,
269 cs
, rulenum
, verbose
);
278 nft_ipv4_delete_entry(struct nft_handle
*h
,
279 const char *chain
, const char *table
,
280 struct iptables_command_state
*cs
,
281 struct xtables_args
*args
, bool verbose
)
286 for (i
= 0; i
< args
->s
.naddrs
; i
++) {
287 cs
->fw
.ip
.src
.s_addr
= args
->s
.addr
.v4
[i
].s_addr
;
288 cs
->fw
.ip
.smsk
.s_addr
= args
->s
.mask
.v4
[i
].s_addr
;
289 for (j
= 0; j
< args
->d
.naddrs
; j
++) {
290 cs
->fw
.ip
.dst
.s_addr
= args
->d
.addr
.v4
[j
].s_addr
;
291 cs
->fw
.ip
.dmsk
.s_addr
= args
->d
.mask
.v4
[j
].s_addr
;
292 ret
= nft_cmd_rule_delete(h
, chain
, table
, cs
, verbose
);
300 nft_ipv4_check_entry(struct nft_handle
*h
,
301 const char *chain
, const char *table
,
302 struct iptables_command_state
*cs
,
303 struct xtables_args
*args
, bool verbose
)
308 for (i
= 0; i
< args
->s
.naddrs
; i
++) {
309 cs
->fw
.ip
.src
.s_addr
= args
->s
.addr
.v4
[i
].s_addr
;
310 cs
->fw
.ip
.smsk
.s_addr
= args
->s
.mask
.v4
[i
].s_addr
;
311 for (j
= 0; j
< args
->d
.naddrs
; j
++) {
312 cs
->fw
.ip
.dst
.s_addr
= args
->d
.addr
.v4
[j
].s_addr
;
313 cs
->fw
.ip
.dmsk
.s_addr
= args
->d
.mask
.v4
[j
].s_addr
;
314 ret
= nft_cmd_rule_check(h
, chain
, table
, cs
, verbose
);
322 nft_ipv4_replace_entry(struct nft_handle
*h
,
323 const char *chain
, const char *table
,
324 struct iptables_command_state
*cs
,
325 struct xtables_args
*args
, bool verbose
,
328 cs
->fw
.ip
.src
.s_addr
= args
->s
.addr
.v4
->s_addr
;
329 cs
->fw
.ip
.dst
.s_addr
= args
->d
.addr
.v4
->s_addr
;
330 cs
->fw
.ip
.smsk
.s_addr
= args
->s
.mask
.v4
->s_addr
;
331 cs
->fw
.ip
.dmsk
.s_addr
= args
->d
.mask
.v4
->s_addr
;
333 return nft_cmd_rule_replace(h
, chain
, table
, cs
, rulenum
, verbose
);
336 struct nft_family_ops nft_family_ops_ipv4
= {
338 .is_same
= nft_ipv4_is_same
,
339 .set_goto_flag
= nft_ipv4_set_goto_flag
,
340 .print_header
= print_header
,
341 .print_rule
= nft_ipv4_print_rule
,
342 .save_rule
= nft_ipv4_save_rule
,
343 .save_chain
= nft_ipv46_save_chain
,
344 .rule_parse
= &nft_ruleparse_ops_ipv4
,
346 .proto_parse
= ipv4_proto_parse
,
347 .post_parse
= ipv4_post_parse
,
348 .option_name
= ip46t_option_name
,
349 .option_invert
= ip46t_option_invert
,
350 .command_default
= command_default
,
351 .print_help
= xtables_printhelp
,
353 .rule_to_cs
= nft_rule_to_iptables_command_state
,
354 .clear_cs
= xtables_clear_iptables_command_state
,
355 .xlate
= nft_ipv4_xlate
,
356 .add_entry
= nft_ipv4_add_entry
,
357 .delete_entry
= nft_ipv4_delete_entry
,
358 .check_entry
= nft_ipv4_check_entry
,
359 .replace_entry
= nft_ipv4_replace_entry
,