diff options
author | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-08-02 14:51:39 -0400 |
---|---|---|
committer | Pablo Neira Ayuso <pablo@netfilter.org> | 2018-08-03 15:12:12 -0400 |
commit | af308b94a2a4a5a27bec9028354c4df444a7c8ba (patch) | |
tree | 2d3082f03ade1bdeec8e276266816128bf27d39d | |
parent | 033eab53fff7acc0f5718dee6fda641734b94416 (diff) |
netfilter: nf_tables: add tunnel support
This patch implements the tunnel object type that can be used to
configure tunnels via metadata template through the existing lightweight
API from the ingress path.
Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
-rw-r--r-- | include/uapi/linux/netfilter/nf_tables.h | 69 | ||||
-rw-r--r-- | net/core/dst.c | 1 | ||||
-rw-r--r-- | net/netfilter/Kconfig | 6 | ||||
-rw-r--r-- | net/netfilter/Makefile | 1 | ||||
-rw-r--r-- | net/netfilter/nft_tunnel.c | 458 |
5 files changed, 534 insertions, 1 deletions
diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index f112ea52dc1a..3ee1198eeac1 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h | |||
@@ -1416,7 +1416,8 @@ enum nft_ct_helper_attributes { | |||
1416 | #define NFT_OBJECT_CT_HELPER 3 | 1416 | #define NFT_OBJECT_CT_HELPER 3 |
1417 | #define NFT_OBJECT_LIMIT 4 | 1417 | #define NFT_OBJECT_LIMIT 4 |
1418 | #define NFT_OBJECT_CONNLIMIT 5 | 1418 | #define NFT_OBJECT_CONNLIMIT 5 |
1419 | #define __NFT_OBJECT_MAX 6 | 1419 | #define NFT_OBJECT_TUNNEL 6 |
1420 | #define __NFT_OBJECT_MAX 7 | ||
1420 | #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) | 1421 | #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) |
1421 | 1422 | ||
1422 | /** | 1423 | /** |
@@ -1580,4 +1581,70 @@ enum nft_ng_types { | |||
1580 | }; | 1581 | }; |
1581 | #define NFT_NG_MAX (__NFT_NG_MAX - 1) | 1582 | #define NFT_NG_MAX (__NFT_NG_MAX - 1) |
1582 | 1583 | ||
1584 | enum nft_tunnel_key_ip_attributes { | ||
1585 | NFTA_TUNNEL_KEY_IP_UNSPEC, | ||
1586 | NFTA_TUNNEL_KEY_IP_SRC, | ||
1587 | NFTA_TUNNEL_KEY_IP_DST, | ||
1588 | __NFTA_TUNNEL_KEY_IP_MAX | ||
1589 | }; | ||
1590 | #define NFTA_TUNNEL_KEY_IP_MAX (__NFTA_TUNNEL_KEY_IP_MAX - 1) | ||
1591 | |||
1592 | enum nft_tunnel_ip6_attributes { | ||
1593 | NFTA_TUNNEL_KEY_IP6_UNSPEC, | ||
1594 | NFTA_TUNNEL_KEY_IP6_SRC, | ||
1595 | NFTA_TUNNEL_KEY_IP6_DST, | ||
1596 | NFTA_TUNNEL_KEY_IP6_FLOWLABEL, | ||
1597 | __NFTA_TUNNEL_KEY_IP6_MAX | ||
1598 | }; | ||
1599 | #define NFTA_TUNNEL_KEY_IP6_MAX (__NFTA_TUNNEL_KEY_IP6_MAX - 1) | ||
1600 | |||
1601 | enum nft_tunnel_opts_attributes { | ||
1602 | NFTA_TUNNEL_KEY_OPTS_UNSPEC, | ||
1603 | NFTA_TUNNEL_KEY_OPTS_VXLAN, | ||
1604 | NFTA_TUNNEL_KEY_OPTS_ERSPAN, | ||
1605 | __NFTA_TUNNEL_KEY_OPTS_MAX | ||
1606 | }; | ||
1607 | #define NFTA_TUNNEL_KEY_OPTS_MAX (__NFTA_TUNNEL_KEY_OPTS_MAX - 1) | ||
1608 | |||
1609 | enum nft_tunnel_opts_vxlan_attributes { | ||
1610 | NFTA_TUNNEL_KEY_VXLAN_UNSPEC, | ||
1611 | NFTA_TUNNEL_KEY_VXLAN_GBP, | ||
1612 | __NFTA_TUNNEL_KEY_VXLAN_MAX | ||
1613 | }; | ||
1614 | #define NFTA_TUNNEL_KEY_VXLAN_MAX (__NFTA_TUNNEL_KEY_VXLAN_MAX - 1) | ||
1615 | |||
1616 | enum nft_tunnel_opts_erspan_attributes { | ||
1617 | NFTA_TUNNEL_KEY_ERSPAN_UNSPEC, | ||
1618 | NFTA_TUNNEL_KEY_ERSPAN_VERSION, | ||
1619 | NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX, | ||
1620 | NFTA_TUNNEL_KEY_ERSPAN_V2_HWID, | ||
1621 | NFTA_TUNNEL_KEY_ERSPAN_V2_DIR, | ||
1622 | __NFTA_TUNNEL_KEY_ERSPAN_MAX | ||
1623 | }; | ||
1624 | #define NFTA_TUNNEL_KEY_ERSPAN_MAX (__NFTA_TUNNEL_KEY_ERSPAN_MAX - 1) | ||
1625 | |||
1626 | enum nft_tunnel_flags { | ||
1627 | NFT_TUNNEL_F_ZERO_CSUM_TX = (1 << 0), | ||
1628 | NFT_TUNNEL_F_DONT_FRAGMENT = (1 << 1), | ||
1629 | NFT_TUNNEL_F_SEQ_NUMBER = (1 << 2), | ||
1630 | }; | ||
1631 | #define NFT_TUNNEL_F_MASK (NFT_TUNNEL_F_ZERO_CSUM_TX | \ | ||
1632 | NFT_TUNNEL_F_DONT_FRAGMENT | \ | ||
1633 | NFT_TUNNEL_F_SEQ_NUMBER) | ||
1634 | |||
1635 | enum nft_tunnel_key_attributes { | ||
1636 | NFTA_TUNNEL_KEY_UNSPEC, | ||
1637 | NFTA_TUNNEL_KEY_ID, | ||
1638 | NFTA_TUNNEL_KEY_IP, | ||
1639 | NFTA_TUNNEL_KEY_IP6, | ||
1640 | NFTA_TUNNEL_KEY_FLAGS, | ||
1641 | NFTA_TUNNEL_KEY_TOS, | ||
1642 | NFTA_TUNNEL_KEY_TTL, | ||
1643 | NFTA_TUNNEL_KEY_SPORT, | ||
1644 | NFTA_TUNNEL_KEY_DPORT, | ||
1645 | NFTA_TUNNEL_KEY_OPTS, | ||
1646 | __NFTA_TUNNEL_KEY_MAX | ||
1647 | }; | ||
1648 | #define NFTA_TUNNEL_KEY_MAX (__NFTA_TUNNEL_KEY_MAX - 1) | ||
1649 | |||
1583 | #endif /* _LINUX_NF_TABLES_H */ | 1650 | #endif /* _LINUX_NF_TABLES_H */ |
diff --git a/net/core/dst.c b/net/core/dst.c index 2d9b37f8944a..81ccf20e2826 100644 --- a/net/core/dst.c +++ b/net/core/dst.c | |||
@@ -307,6 +307,7 @@ void metadata_dst_free(struct metadata_dst *md_dst) | |||
307 | #endif | 307 | #endif |
308 | kfree(md_dst); | 308 | kfree(md_dst); |
309 | } | 309 | } |
310 | EXPORT_SYMBOL_GPL(metadata_dst_free); | ||
310 | 311 | ||
311 | struct metadata_dst __percpu * | 312 | struct metadata_dst __percpu * |
312 | metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags) | 313 | metadata_dst_alloc_percpu(u8 optslen, enum metadata_type type, gfp_t flags) |
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 55e399d5af10..654588088676 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig | |||
@@ -559,6 +559,12 @@ config NFT_NAT | |||
559 | This option adds the "nat" expression that you can use to perform | 559 | This option adds the "nat" expression that you can use to perform |
560 | typical Network Address Translation (NAT) packet transformations. | 560 | typical Network Address Translation (NAT) packet transformations. |
561 | 561 | ||
562 | config NFT_TUNNEL | ||
563 | tristate "Netfilter nf_tables tunnel module" | ||
564 | help | ||
565 | This option adds the "tunnel" expression that you can use to set | ||
566 | tunneling policies. | ||
567 | |||
562 | config NFT_OBJREF | 568 | config NFT_OBJREF |
563 | tristate "Netfilter nf_tables stateful object reference module" | 569 | tristate "Netfilter nf_tables stateful object reference module" |
564 | help | 570 | help |
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index cf61615cc529..16895e045b66 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile | |||
@@ -101,6 +101,7 @@ obj-$(CONFIG_NFT_QUEUE) += nft_queue.o | |||
101 | obj-$(CONFIG_NFT_QUOTA) += nft_quota.o | 101 | obj-$(CONFIG_NFT_QUOTA) += nft_quota.o |
102 | obj-$(CONFIG_NFT_REJECT) += nft_reject.o | 102 | obj-$(CONFIG_NFT_REJECT) += nft_reject.o |
103 | obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o | 103 | obj-$(CONFIG_NFT_REJECT_INET) += nft_reject_inet.o |
104 | obj-$(CONFIG_NFT_TUNNEL) += nft_tunnel.o | ||
104 | obj-$(CONFIG_NFT_COUNTER) += nft_counter.o | 105 | obj-$(CONFIG_NFT_COUNTER) += nft_counter.o |
105 | obj-$(CONFIG_NFT_LOG) += nft_log.o | 106 | obj-$(CONFIG_NFT_LOG) += nft_log.o |
106 | obj-$(CONFIG_NFT_MASQ) += nft_masq.o | 107 | obj-$(CONFIG_NFT_MASQ) += nft_masq.o |
diff --git a/net/netfilter/nft_tunnel.c b/net/netfilter/nft_tunnel.c new file mode 100644 index 000000000000..715613d99c20 --- /dev/null +++ b/net/netfilter/nft_tunnel.c | |||
@@ -0,0 +1,458 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0 */ | ||
2 | #include <linux/kernel.h> | ||
3 | #include <linux/init.h> | ||
4 | #include <linux/module.h> | ||
5 | #include <linux/seqlock.h> | ||
6 | #include <linux/netlink.h> | ||
7 | #include <linux/netfilter.h> | ||
8 | #include <linux/netfilter/nf_tables.h> | ||
9 | #include <net/netfilter/nf_tables.h> | ||
10 | #include <net/dst_metadata.h> | ||
11 | #include <net/ip_tunnels.h> | ||
12 | #include <net/vxlan.h> | ||
13 | #include <net/erspan.h> | ||
14 | |||
15 | struct nft_tunnel_opts { | ||
16 | union { | ||
17 | struct vxlan_metadata vxlan; | ||
18 | struct erspan_metadata erspan; | ||
19 | } u; | ||
20 | u32 len; | ||
21 | u32 flags; | ||
22 | }; | ||
23 | |||
24 | struct nft_tunnel_obj { | ||
25 | struct metadata_dst *md; | ||
26 | struct nft_tunnel_opts opts; | ||
27 | }; | ||
28 | |||
29 | static const struct nla_policy nft_tunnel_ip_policy[NFTA_TUNNEL_KEY_IP_MAX + 1] = { | ||
30 | [NFTA_TUNNEL_KEY_IP_SRC] = { .type = NLA_U32 }, | ||
31 | [NFTA_TUNNEL_KEY_IP_DST] = { .type = NLA_U32 }, | ||
32 | }; | ||
33 | |||
34 | static int nft_tunnel_obj_ip_init(const struct nft_ctx *ctx, | ||
35 | const struct nlattr *attr, | ||
36 | struct ip_tunnel_info *info) | ||
37 | { | ||
38 | struct nlattr *tb[NFTA_TUNNEL_KEY_IP_MAX + 1]; | ||
39 | int err; | ||
40 | |||
41 | err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_IP_MAX, attr, | ||
42 | nft_tunnel_ip_policy, NULL); | ||
43 | if (err < 0) | ||
44 | return err; | ||
45 | |||
46 | if (!tb[NFTA_TUNNEL_KEY_IP_DST]) | ||
47 | return -EINVAL; | ||
48 | |||
49 | if (tb[NFTA_TUNNEL_KEY_IP_SRC]) | ||
50 | info->key.u.ipv4.src = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_SRC]); | ||
51 | if (tb[NFTA_TUNNEL_KEY_IP_DST]) | ||
52 | info->key.u.ipv4.dst = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP_DST]); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static const struct nla_policy nft_tunnel_ip6_policy[NFTA_TUNNEL_KEY_IP6_MAX + 1] = { | ||
58 | [NFTA_TUNNEL_KEY_IP6_SRC] = { .len = sizeof(struct in6_addr), }, | ||
59 | [NFTA_TUNNEL_KEY_IP6_DST] = { .len = sizeof(struct in6_addr), }, | ||
60 | [NFTA_TUNNEL_KEY_IP6_FLOWLABEL] = { .type = NLA_U32, } | ||
61 | }; | ||
62 | |||
63 | static int nft_tunnel_obj_ip6_init(const struct nft_ctx *ctx, | ||
64 | const struct nlattr *attr, | ||
65 | struct ip_tunnel_info *info) | ||
66 | { | ||
67 | struct nlattr *tb[NFTA_TUNNEL_KEY_IP6_MAX + 1]; | ||
68 | int err; | ||
69 | |||
70 | err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_IP6_MAX, attr, | ||
71 | nft_tunnel_ip6_policy, NULL); | ||
72 | if (err < 0) | ||
73 | return err; | ||
74 | |||
75 | if (!tb[NFTA_TUNNEL_KEY_IP6_DST]) | ||
76 | return -EINVAL; | ||
77 | |||
78 | if (tb[NFTA_TUNNEL_KEY_IP6_SRC]) { | ||
79 | memcpy(&info->key.u.ipv6.src, | ||
80 | nla_data(tb[NFTA_TUNNEL_KEY_IP6_SRC]), | ||
81 | sizeof(struct in6_addr)); | ||
82 | } | ||
83 | if (tb[NFTA_TUNNEL_KEY_IP6_DST]) { | ||
84 | memcpy(&info->key.u.ipv6.dst, | ||
85 | nla_data(tb[NFTA_TUNNEL_KEY_IP6_DST]), | ||
86 | sizeof(struct in6_addr)); | ||
87 | } | ||
88 | if (tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]) | ||
89 | info->key.label = nla_get_be32(tb[NFTA_TUNNEL_KEY_IP6_FLOWLABEL]); | ||
90 | |||
91 | info->mode |= IP_TUNNEL_INFO_IPV6; | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | static const struct nla_policy nft_tunnel_opts_vxlan_policy[NFTA_TUNNEL_KEY_VXLAN_MAX + 1] = { | ||
97 | [NFTA_TUNNEL_KEY_VXLAN_GBP] = { .type = NLA_U32 }, | ||
98 | }; | ||
99 | |||
100 | static int nft_tunnel_obj_vxlan_init(const struct nlattr *attr, | ||
101 | struct nft_tunnel_opts *opts) | ||
102 | { | ||
103 | struct nlattr *tb[NFTA_TUNNEL_KEY_VXLAN_MAX + 1]; | ||
104 | int err; | ||
105 | |||
106 | err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_VXLAN_MAX, attr, | ||
107 | nft_tunnel_opts_vxlan_policy, NULL); | ||
108 | if (err < 0) | ||
109 | return err; | ||
110 | |||
111 | if (!tb[NFTA_TUNNEL_KEY_VXLAN_GBP]) | ||
112 | return -EINVAL; | ||
113 | |||
114 | opts->u.vxlan.gbp = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_VXLAN_GBP])); | ||
115 | |||
116 | opts->len = sizeof(struct vxlan_metadata); | ||
117 | opts->flags = TUNNEL_VXLAN_OPT; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static const struct nla_policy nft_tunnel_opts_erspan_policy[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1] = { | ||
123 | [NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX] = { .type = NLA_U32 }, | ||
124 | [NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] = { .type = NLA_U8 }, | ||
125 | [NFTA_TUNNEL_KEY_ERSPAN_V2_HWID] = { .type = NLA_U8 }, | ||
126 | }; | ||
127 | |||
128 | static int nft_tunnel_obj_erspan_init(const struct nlattr *attr, | ||
129 | struct nft_tunnel_opts *opts) | ||
130 | { | ||
131 | struct nlattr *tb[NFTA_TUNNEL_KEY_ERSPAN_MAX + 1]; | ||
132 | uint8_t hwid, dir; | ||
133 | int err, version; | ||
134 | |||
135 | err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_ERSPAN_MAX, attr, | ||
136 | nft_tunnel_opts_erspan_policy, NULL); | ||
137 | if (err < 0) | ||
138 | return err; | ||
139 | |||
140 | version = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_VERSION])); | ||
141 | switch (version) { | ||
142 | case ERSPAN_VERSION: | ||
143 | if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]) | ||
144 | return -EINVAL; | ||
145 | |||
146 | opts->u.erspan.u.index = | ||
147 | nla_get_be32(tb[NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX]); | ||
148 | break; | ||
149 | case ERSPAN_VERSION2: | ||
150 | if (!tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR] || | ||
151 | !tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]) | ||
152 | return -EINVAL; | ||
153 | |||
154 | hwid = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_HWID]); | ||
155 | dir = nla_get_u8(tb[NFTA_TUNNEL_KEY_ERSPAN_V2_DIR]); | ||
156 | |||
157 | set_hwid(&opts->u.erspan.u.md2, hwid); | ||
158 | opts->u.erspan.u.md2.dir = dir; | ||
159 | break; | ||
160 | default: | ||
161 | return -EOPNOTSUPP; | ||
162 | } | ||
163 | opts->u.erspan.version = version; | ||
164 | |||
165 | opts->len = sizeof(struct erspan_metadata); | ||
166 | opts->flags = TUNNEL_ERSPAN_OPT; | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | static const struct nla_policy nft_tunnel_opts_policy[NFTA_TUNNEL_KEY_OPTS_MAX + 1] = { | ||
172 | [NFTA_TUNNEL_KEY_OPTS_VXLAN] = { .type = NLA_NESTED, }, | ||
173 | [NFTA_TUNNEL_KEY_OPTS_ERSPAN] = { .type = NLA_NESTED, }, | ||
174 | }; | ||
175 | |||
176 | static int nft_tunnel_obj_opts_init(const struct nft_ctx *ctx, | ||
177 | const struct nlattr *attr, | ||
178 | struct ip_tunnel_info *info, | ||
179 | struct nft_tunnel_opts *opts) | ||
180 | { | ||
181 | struct nlattr *tb[NFTA_TUNNEL_KEY_OPTS_MAX + 1]; | ||
182 | int err; | ||
183 | |||
184 | err = nla_parse_nested(tb, NFTA_TUNNEL_KEY_OPTS_MAX, attr, | ||
185 | nft_tunnel_opts_policy, NULL); | ||
186 | if (err < 0) | ||
187 | return err; | ||
188 | |||
189 | if (tb[NFTA_TUNNEL_KEY_OPTS_VXLAN]) { | ||
190 | err = nft_tunnel_obj_vxlan_init(tb[NFTA_TUNNEL_KEY_OPTS_VXLAN], | ||
191 | opts); | ||
192 | } else if (tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN]) { | ||
193 | err = nft_tunnel_obj_erspan_init(tb[NFTA_TUNNEL_KEY_OPTS_ERSPAN], | ||
194 | opts); | ||
195 | } else { | ||
196 | return -EOPNOTSUPP; | ||
197 | } | ||
198 | |||
199 | return err; | ||
200 | } | ||
201 | |||
202 | static const struct nla_policy nft_tunnel_key_policy[NFTA_TUNNEL_KEY_MAX + 1] = { | ||
203 | [NFTA_TUNNEL_KEY_IP] = { .type = NLA_NESTED, }, | ||
204 | [NFTA_TUNNEL_KEY_IP6] = { .type = NLA_NESTED, }, | ||
205 | [NFTA_TUNNEL_KEY_ID] = { .type = NLA_U32, }, | ||
206 | [NFTA_TUNNEL_KEY_FLAGS] = { .type = NLA_U32, }, | ||
207 | [NFTA_TUNNEL_KEY_TOS] = { .type = NLA_U8, }, | ||
208 | [NFTA_TUNNEL_KEY_TTL] = { .type = NLA_U8, }, | ||
209 | [NFTA_TUNNEL_KEY_OPTS] = { .type = NLA_NESTED, }, | ||
210 | }; | ||
211 | |||
212 | static int nft_tunnel_obj_init(const struct nft_ctx *ctx, | ||
213 | const struct nlattr * const tb[], | ||
214 | struct nft_object *obj) | ||
215 | { | ||
216 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | ||
217 | struct ip_tunnel_info info; | ||
218 | struct metadata_dst *md; | ||
219 | int err; | ||
220 | |||
221 | if (!tb[NFTA_TUNNEL_KEY_ID]) | ||
222 | return -EINVAL; | ||
223 | |||
224 | memset(&info, 0, sizeof(info)); | ||
225 | info.mode = IP_TUNNEL_INFO_TX; | ||
226 | info.key.tun_id = key32_to_tunnel_id(nla_get_be32(tb[NFTA_TUNNEL_KEY_ID])); | ||
227 | info.key.tun_flags = TUNNEL_KEY | TUNNEL_CSUM | TUNNEL_NOCACHE; | ||
228 | |||
229 | if (tb[NFTA_TUNNEL_KEY_IP]) { | ||
230 | err = nft_tunnel_obj_ip_init(ctx, tb[NFTA_TUNNEL_KEY_IP], &info); | ||
231 | if (err < 0) | ||
232 | return err; | ||
233 | } else if (tb[NFTA_TUNNEL_KEY_IP6]) { | ||
234 | err = nft_tunnel_obj_ip6_init(ctx, tb[NFTA_TUNNEL_KEY_IP6], &info); | ||
235 | if (err < 0) | ||
236 | return err; | ||
237 | } else { | ||
238 | return -EINVAL; | ||
239 | } | ||
240 | |||
241 | if (tb[NFTA_TUNNEL_KEY_SPORT]) { | ||
242 | info.key.tp_src = | ||
243 | ntohs(nla_get_be16(tb[NFTA_TUNNEL_KEY_SPORT])); | ||
244 | } | ||
245 | if (tb[NFTA_TUNNEL_KEY_DPORT]) { | ||
246 | info.key.tp_dst = | ||
247 | ntohs(nla_get_be16(tb[NFTA_TUNNEL_KEY_DPORT])); | ||
248 | } | ||
249 | |||
250 | if (tb[NFTA_TUNNEL_KEY_FLAGS]) { | ||
251 | u32 tun_flags; | ||
252 | |||
253 | tun_flags = ntohl(nla_get_be32(tb[NFTA_TUNNEL_KEY_FLAGS])); | ||
254 | if (tun_flags & ~NFT_TUNNEL_F_MASK) | ||
255 | return -EOPNOTSUPP; | ||
256 | |||
257 | if (tun_flags & NFT_TUNNEL_F_ZERO_CSUM_TX) | ||
258 | info.key.tun_flags &= ~TUNNEL_CSUM; | ||
259 | if (tun_flags & NFT_TUNNEL_F_DONT_FRAGMENT) | ||
260 | info.key.tun_flags |= TUNNEL_DONT_FRAGMENT; | ||
261 | if (tun_flags & NFT_TUNNEL_F_SEQ_NUMBER) | ||
262 | info.key.tun_flags |= TUNNEL_SEQ; | ||
263 | } | ||
264 | if (tb[NFTA_TUNNEL_KEY_TOS]) | ||
265 | info.key.tos = nla_get_u8(tb[NFTA_TUNNEL_KEY_TOS]); | ||
266 | if (tb[NFTA_TUNNEL_KEY_TTL]) | ||
267 | info.key.ttl = nla_get_u8(tb[NFTA_TUNNEL_KEY_TTL]); | ||
268 | else | ||
269 | info.key.ttl = U8_MAX; | ||
270 | |||
271 | if (tb[NFTA_TUNNEL_KEY_OPTS]) { | ||
272 | err = nft_tunnel_obj_opts_init(ctx, tb[NFTA_TUNNEL_KEY_OPTS], | ||
273 | &info, &priv->opts); | ||
274 | if (err < 0) | ||
275 | return err; | ||
276 | } | ||
277 | |||
278 | md = metadata_dst_alloc(priv->opts.len, METADATA_IP_TUNNEL, GFP_KERNEL); | ||
279 | if (!md) | ||
280 | return -ENOMEM; | ||
281 | |||
282 | memcpy(&md->u.tun_info, &info, sizeof(info)); | ||
283 | ip_tunnel_info_opts_set(&md->u.tun_info, &priv->opts.u, priv->opts.len, | ||
284 | priv->opts.flags); | ||
285 | priv->md = md; | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | static inline void nft_tunnel_obj_eval(struct nft_object *obj, | ||
291 | struct nft_regs *regs, | ||
292 | const struct nft_pktinfo *pkt) | ||
293 | { | ||
294 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | ||
295 | struct sk_buff *skb = pkt->skb; | ||
296 | |||
297 | skb_dst_drop(skb); | ||
298 | dst_hold((struct dst_entry *) priv->md); | ||
299 | skb_dst_set(skb, (struct dst_entry *) priv->md); | ||
300 | } | ||
301 | |||
302 | static int nft_tunnel_ip_dump(struct sk_buff *skb, struct ip_tunnel_info *info) | ||
303 | { | ||
304 | struct nlattr *nest; | ||
305 | |||
306 | if (info->mode & IP_TUNNEL_INFO_IPV6) { | ||
307 | nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_IP6); | ||
308 | if (!nest) | ||
309 | return -1; | ||
310 | |||
311 | if (nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_SRC, &info->key.u.ipv6.src) < 0 || | ||
312 | nla_put_in6_addr(skb, NFTA_TUNNEL_KEY_IP6_DST, &info->key.u.ipv6.dst) < 0 || | ||
313 | nla_put_be32(skb, NFTA_TUNNEL_KEY_IP6_FLOWLABEL, info->key.label)) | ||
314 | return -1; | ||
315 | |||
316 | nla_nest_end(skb, nest); | ||
317 | } else { | ||
318 | nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_IP); | ||
319 | if (!nest) | ||
320 | return -1; | ||
321 | |||
322 | if (nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_SRC, info->key.u.ipv4.src) < 0 || | ||
323 | nla_put_in_addr(skb, NFTA_TUNNEL_KEY_IP_DST, info->key.u.ipv4.dst) < 0) | ||
324 | return -1; | ||
325 | |||
326 | nla_nest_end(skb, nest); | ||
327 | } | ||
328 | |||
329 | return 0; | ||
330 | } | ||
331 | |||
332 | static int nft_tunnel_opts_dump(struct sk_buff *skb, | ||
333 | struct nft_tunnel_obj *priv) | ||
334 | { | ||
335 | struct nft_tunnel_opts *opts = &priv->opts; | ||
336 | struct nlattr *nest; | ||
337 | |||
338 | nest = nla_nest_start(skb, NFTA_TUNNEL_KEY_OPTS); | ||
339 | if (!nest) | ||
340 | return -1; | ||
341 | |||
342 | if (opts->flags & TUNNEL_VXLAN_OPT) { | ||
343 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_VXLAN_GBP, | ||
344 | htonl(opts->u.vxlan.gbp))) | ||
345 | return -1; | ||
346 | } else if (opts->flags & TUNNEL_ERSPAN_OPT) { | ||
347 | switch (opts->u.erspan.version) { | ||
348 | case ERSPAN_VERSION: | ||
349 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ERSPAN_V1_INDEX, | ||
350 | opts->u.erspan.u.index)) | ||
351 | return -1; | ||
352 | break; | ||
353 | case ERSPAN_VERSION2: | ||
354 | if (nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_HWID, | ||
355 | get_hwid(&opts->u.erspan.u.md2)) || | ||
356 | nla_put_u8(skb, NFTA_TUNNEL_KEY_ERSPAN_V2_DIR, | ||
357 | opts->u.erspan.u.md2.dir)) | ||
358 | return -1; | ||
359 | break; | ||
360 | } | ||
361 | } | ||
362 | nla_nest_end(skb, nest); | ||
363 | |||
364 | return 0; | ||
365 | } | ||
366 | |||
367 | static int nft_tunnel_ports_dump(struct sk_buff *skb, | ||
368 | struct ip_tunnel_info *info) | ||
369 | { | ||
370 | if (nla_put_be16(skb, NFTA_TUNNEL_KEY_SPORT, htons(info->key.tp_src)) < 0 || | ||
371 | nla_put_be16(skb, NFTA_TUNNEL_KEY_DPORT, htons(info->key.tp_dst)) < 0) | ||
372 | return -1; | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int nft_tunnel_flags_dump(struct sk_buff *skb, | ||
378 | struct ip_tunnel_info *info) | ||
379 | { | ||
380 | u32 flags = 0; | ||
381 | |||
382 | if (info->key.tun_flags & TUNNEL_DONT_FRAGMENT) | ||
383 | flags |= NFT_TUNNEL_F_DONT_FRAGMENT; | ||
384 | if (!(info->key.tun_flags & TUNNEL_CSUM)) | ||
385 | flags |= NFT_TUNNEL_F_ZERO_CSUM_TX; | ||
386 | if (info->key.tun_flags & TUNNEL_SEQ) | ||
387 | flags |= NFT_TUNNEL_F_SEQ_NUMBER; | ||
388 | |||
389 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_FLAGS, htonl(flags)) < 0) | ||
390 | return -1; | ||
391 | |||
392 | return 0; | ||
393 | } | ||
394 | |||
395 | static int nft_tunnel_obj_dump(struct sk_buff *skb, | ||
396 | struct nft_object *obj, bool reset) | ||
397 | { | ||
398 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | ||
399 | struct ip_tunnel_info *info = &priv->md->u.tun_info; | ||
400 | |||
401 | if (nla_put_be32(skb, NFTA_TUNNEL_KEY_ID, | ||
402 | tunnel_id_to_key32(info->key.tun_id)) || | ||
403 | nft_tunnel_ip_dump(skb, info) < 0 || | ||
404 | nft_tunnel_ports_dump(skb, info) < 0 || | ||
405 | nft_tunnel_flags_dump(skb, info) < 0 || | ||
406 | nla_put_u8(skb, NFTA_TUNNEL_KEY_TOS, info->key.tos) || | ||
407 | nla_put_u8(skb, NFTA_TUNNEL_KEY_TTL, info->key.ttl) || | ||
408 | nft_tunnel_opts_dump(skb, priv) < 0) | ||
409 | goto nla_put_failure; | ||
410 | |||
411 | return 0; | ||
412 | |||
413 | nla_put_failure: | ||
414 | return -1; | ||
415 | } | ||
416 | |||
417 | static void nft_tunnel_obj_destroy(const struct nft_ctx *ctx, | ||
418 | struct nft_object *obj) | ||
419 | { | ||
420 | struct nft_tunnel_obj *priv = nft_obj_data(obj); | ||
421 | |||
422 | metadata_dst_free(priv->md); | ||
423 | } | ||
424 | |||
425 | static struct nft_object_type nft_tunnel_obj_type; | ||
426 | static const struct nft_object_ops nft_tunnel_obj_ops = { | ||
427 | .type = &nft_tunnel_obj_type, | ||
428 | .size = sizeof(struct nft_tunnel_obj), | ||
429 | .eval = nft_tunnel_obj_eval, | ||
430 | .init = nft_tunnel_obj_init, | ||
431 | .destroy = nft_tunnel_obj_destroy, | ||
432 | .dump = nft_tunnel_obj_dump, | ||
433 | }; | ||
434 | |||
435 | static struct nft_object_type nft_tunnel_obj_type __read_mostly = { | ||
436 | .type = NFT_OBJECT_TUNNEL, | ||
437 | .ops = &nft_tunnel_obj_ops, | ||
438 | .maxattr = NFTA_TUNNEL_KEY_MAX, | ||
439 | .policy = nft_tunnel_key_policy, | ||
440 | .owner = THIS_MODULE, | ||
441 | }; | ||
442 | |||
443 | static int __init nft_tunnel_module_init(void) | ||
444 | { | ||
445 | return nft_register_obj(&nft_tunnel_obj_type); | ||
446 | } | ||
447 | |||
448 | static void __exit nft_tunnel_module_exit(void) | ||
449 | { | ||
450 | nft_unregister_obj(&nft_tunnel_obj_type); | ||
451 | } | ||
452 | |||
453 | module_init(nft_tunnel_module_init); | ||
454 | module_exit(nft_tunnel_module_exit); | ||
455 | |||
456 | MODULE_LICENSE("GPL"); | ||
457 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | ||
458 | MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_TUNNEL); | ||