Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (c) 2016, Amir Vadai <amir@vadai.me> |
| 3 | * Copyright (c) 2016, Mellanox Technologies. All rights reserved. |
| 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 | |
| 11 | #include <linux/module.h> |
| 12 | #include <linux/init.h> |
| 13 | #include <linux/kernel.h> |
| 14 | #include <linux/skbuff.h> |
| 15 | #include <linux/rtnetlink.h> |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 16 | #include <net/geneve.h> |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 17 | #include <net/netlink.h> |
| 18 | #include <net/pkt_sched.h> |
| 19 | #include <net/dst.h> |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 20 | |
| 21 | #include <linux/tc_act/tc_tunnel_key.h> |
| 22 | #include <net/tc_act/tc_tunnel_key.h> |
| 23 | |
Alexey Dobriyan | c7d03a0 | 2016-11-17 04:58:21 +0300 | [diff] [blame] | 24 | static unsigned int tunnel_key_net_id; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 25 | static struct tc_action_ops act_tunnel_key_ops; |
| 26 | |
| 27 | static int tunnel_key_act(struct sk_buff *skb, const struct tc_action *a, |
| 28 | struct tcf_result *res) |
| 29 | { |
| 30 | struct tcf_tunnel_key *t = to_tunnel_key(a); |
| 31 | struct tcf_tunnel_key_params *params; |
| 32 | int action; |
| 33 | |
| 34 | rcu_read_lock(); |
| 35 | |
| 36 | params = rcu_dereference(t->params); |
| 37 | |
| 38 | tcf_lastuse_update(&t->tcf_tm); |
| 39 | bstats_cpu_update(this_cpu_ptr(t->common.cpu_bstats), skb); |
| 40 | action = params->action; |
| 41 | |
| 42 | switch (params->tcft_action) { |
| 43 | case TCA_TUNNEL_KEY_ACT_RELEASE: |
| 44 | skb_dst_drop(skb); |
| 45 | break; |
| 46 | case TCA_TUNNEL_KEY_ACT_SET: |
| 47 | skb_dst_drop(skb); |
| 48 | skb_dst_set(skb, dst_clone(¶ms->tcft_enc_metadata->dst)); |
| 49 | break; |
| 50 | default: |
| 51 | WARN_ONCE(1, "Bad tunnel_key action %d.\n", |
| 52 | params->tcft_action); |
| 53 | break; |
| 54 | } |
| 55 | |
| 56 | rcu_read_unlock(); |
| 57 | |
| 58 | return action; |
| 59 | } |
| 60 | |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 61 | static const struct nla_policy |
| 62 | enc_opts_policy[TCA_TUNNEL_KEY_ENC_OPTS_MAX + 1] = { |
| 63 | [TCA_TUNNEL_KEY_ENC_OPTS_GENEVE] = { .type = NLA_NESTED }, |
| 64 | }; |
| 65 | |
| 66 | static const struct nla_policy |
| 67 | geneve_opt_policy[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1] = { |
| 68 | [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] = { .type = NLA_U16 }, |
| 69 | [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] = { .type = NLA_U8 }, |
| 70 | [TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA] = { .type = NLA_BINARY, |
| 71 | .len = 128 }, |
| 72 | }; |
| 73 | |
| 74 | static int |
| 75 | tunnel_key_copy_geneve_opt(const struct nlattr *nla, void *dst, int dst_len, |
| 76 | struct netlink_ext_ack *extack) |
| 77 | { |
| 78 | struct nlattr *tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX + 1]; |
| 79 | int err, data_len, opt_len; |
| 80 | u8 *data; |
| 81 | |
| 82 | err = nla_parse_nested(tb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_MAX, |
| 83 | nla, geneve_opt_policy, extack); |
| 84 | if (err < 0) |
| 85 | return err; |
| 86 | |
| 87 | if (!tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS] || |
| 88 | !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE] || |
| 89 | !tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]) { |
| 90 | NL_SET_ERR_MSG(extack, "Missing tunnel key geneve option class, type or data"); |
| 91 | return -EINVAL; |
| 92 | } |
| 93 | |
| 94 | data = nla_data(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]); |
| 95 | data_len = nla_len(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA]); |
| 96 | if (data_len < 4) { |
| 97 | NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is less than 4 bytes long"); |
| 98 | return -ERANGE; |
| 99 | } |
| 100 | if (data_len % 4) { |
| 101 | NL_SET_ERR_MSG(extack, "Tunnel key geneve option data is not a multiple of 4 bytes long"); |
| 102 | return -ERANGE; |
| 103 | } |
| 104 | |
| 105 | opt_len = sizeof(struct geneve_opt) + data_len; |
| 106 | if (dst) { |
| 107 | struct geneve_opt *opt = dst; |
| 108 | |
| 109 | WARN_ON(dst_len < opt_len); |
| 110 | |
| 111 | opt->opt_class = |
| 112 | nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS]); |
| 113 | opt->type = nla_get_u8(tb[TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE]); |
| 114 | opt->length = data_len / 4; /* length is in units of 4 bytes */ |
| 115 | opt->r1 = 0; |
| 116 | opt->r2 = 0; |
| 117 | opt->r3 = 0; |
| 118 | |
| 119 | memcpy(opt + 1, data, data_len); |
| 120 | } |
| 121 | |
| 122 | return opt_len; |
| 123 | } |
| 124 | |
| 125 | static int tunnel_key_copy_opts(const struct nlattr *nla, u8 *dst, |
| 126 | int dst_len, struct netlink_ext_ack *extack) |
| 127 | { |
| 128 | int err, rem, opt_len, len = nla_len(nla), opts_len = 0; |
| 129 | const struct nlattr *attr, *head = nla_data(nla); |
| 130 | |
| 131 | err = nla_validate(head, len, TCA_TUNNEL_KEY_ENC_OPTS_MAX, |
| 132 | enc_opts_policy, extack); |
| 133 | if (err) |
| 134 | return err; |
| 135 | |
| 136 | nla_for_each_attr(attr, head, len, rem) { |
| 137 | switch (nla_type(attr)) { |
| 138 | case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE: |
| 139 | opt_len = tunnel_key_copy_geneve_opt(attr, dst, |
| 140 | dst_len, extack); |
| 141 | if (opt_len < 0) |
| 142 | return opt_len; |
| 143 | opts_len += opt_len; |
| 144 | if (dst) { |
| 145 | dst_len -= opt_len; |
| 146 | dst += opt_len; |
| 147 | } |
| 148 | break; |
| 149 | } |
| 150 | } |
| 151 | |
| 152 | if (!opts_len) { |
| 153 | NL_SET_ERR_MSG(extack, "Empty list of tunnel options"); |
| 154 | return -EINVAL; |
| 155 | } |
| 156 | |
| 157 | if (rem > 0) { |
| 158 | NL_SET_ERR_MSG(extack, "Trailing data after parsing tunnel key options attributes"); |
| 159 | return -EINVAL; |
| 160 | } |
| 161 | |
| 162 | return opts_len; |
| 163 | } |
| 164 | |
| 165 | static int tunnel_key_get_opts_len(struct nlattr *nla, |
| 166 | struct netlink_ext_ack *extack) |
| 167 | { |
| 168 | return tunnel_key_copy_opts(nla, NULL, 0, extack); |
| 169 | } |
| 170 | |
| 171 | static int tunnel_key_opts_set(struct nlattr *nla, struct ip_tunnel_info *info, |
| 172 | int opts_len, struct netlink_ext_ack *extack) |
| 173 | { |
| 174 | info->options_len = opts_len; |
| 175 | switch (nla_type(nla_data(nla))) { |
| 176 | case TCA_TUNNEL_KEY_ENC_OPTS_GENEVE: |
| 177 | #if IS_ENABLED(CONFIG_INET) |
| 178 | info->key.tun_flags |= TUNNEL_GENEVE_OPT; |
| 179 | return tunnel_key_copy_opts(nla, ip_tunnel_info_opts(info), |
| 180 | opts_len, extack); |
| 181 | #else |
| 182 | return -EAFNOSUPPORT; |
| 183 | #endif |
| 184 | default: |
| 185 | NL_SET_ERR_MSG(extack, "Cannot set tunnel options for unknown tunnel type"); |
| 186 | return -EINVAL; |
| 187 | } |
| 188 | } |
| 189 | |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 190 | static const struct nla_policy tunnel_key_policy[TCA_TUNNEL_KEY_MAX + 1] = { |
| 191 | [TCA_TUNNEL_KEY_PARMS] = { .len = sizeof(struct tc_tunnel_key) }, |
| 192 | [TCA_TUNNEL_KEY_ENC_IPV4_SRC] = { .type = NLA_U32 }, |
| 193 | [TCA_TUNNEL_KEY_ENC_IPV4_DST] = { .type = NLA_U32 }, |
| 194 | [TCA_TUNNEL_KEY_ENC_IPV6_SRC] = { .len = sizeof(struct in6_addr) }, |
| 195 | [TCA_TUNNEL_KEY_ENC_IPV6_DST] = { .len = sizeof(struct in6_addr) }, |
| 196 | [TCA_TUNNEL_KEY_ENC_KEY_ID] = { .type = NLA_U32 }, |
Hadar Hen Zion | 75bfbca | 2016-11-07 15:14:41 +0200 | [diff] [blame] | 197 | [TCA_TUNNEL_KEY_ENC_DST_PORT] = {.type = NLA_U16}, |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 198 | [TCA_TUNNEL_KEY_NO_CSUM] = { .type = NLA_U8 }, |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 199 | [TCA_TUNNEL_KEY_ENC_OPTS] = { .type = NLA_NESTED }, |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 200 | }; |
| 201 | |
| 202 | static int tunnel_key_init(struct net *net, struct nlattr *nla, |
| 203 | struct nlattr *est, struct tc_action **a, |
Alexander Aring | 589dad6 | 2018-02-15 10:54:56 -0500 | [diff] [blame] | 204 | int ovr, int bind, struct netlink_ext_ack *extack) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 205 | { |
| 206 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); |
| 207 | struct nlattr *tb[TCA_TUNNEL_KEY_MAX + 1]; |
| 208 | struct tcf_tunnel_key_params *params_old; |
| 209 | struct tcf_tunnel_key_params *params_new; |
| 210 | struct metadata_dst *metadata = NULL; |
| 211 | struct tc_tunnel_key *parm; |
| 212 | struct tcf_tunnel_key *t; |
| 213 | bool exists = false; |
Hadar Hen Zion | 75bfbca | 2016-11-07 15:14:41 +0200 | [diff] [blame] | 214 | __be16 dst_port = 0; |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 215 | int opts_len = 0; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 216 | __be64 key_id; |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 217 | __be16 flags; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 218 | int ret = 0; |
| 219 | int err; |
| 220 | |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 221 | if (!nla) { |
| 222 | NL_SET_ERR_MSG(extack, "Tunnel requires attributes to be passed"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 223 | return -EINVAL; |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 224 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 225 | |
Johannes Berg | fceb643 | 2017-04-12 14:34:07 +0200 | [diff] [blame] | 226 | err = nla_parse_nested(tb, TCA_TUNNEL_KEY_MAX, nla, tunnel_key_policy, |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 227 | extack); |
| 228 | if (err < 0) { |
| 229 | NL_SET_ERR_MSG(extack, "Failed to parse nested tunnel key attributes"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 230 | return err; |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 231 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 232 | |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 233 | if (!tb[TCA_TUNNEL_KEY_PARMS]) { |
| 234 | NL_SET_ERR_MSG(extack, "Missing tunnel key parameters"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 235 | return -EINVAL; |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 236 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 237 | |
| 238 | parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]); |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 239 | exists = tcf_idr_check(tn, parm->index, a, bind); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 240 | if (exists && bind) |
| 241 | return 0; |
| 242 | |
| 243 | switch (parm->t_action) { |
| 244 | case TCA_TUNNEL_KEY_ACT_RELEASE: |
| 245 | break; |
| 246 | case TCA_TUNNEL_KEY_ACT_SET: |
| 247 | if (!tb[TCA_TUNNEL_KEY_ENC_KEY_ID]) { |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 248 | NL_SET_ERR_MSG(extack, "Missing tunnel key id"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 249 | ret = -EINVAL; |
| 250 | goto err_out; |
| 251 | } |
| 252 | |
| 253 | key_id = key32_to_tunnel_id(nla_get_be32(tb[TCA_TUNNEL_KEY_ENC_KEY_ID])); |
| 254 | |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 255 | flags = TUNNEL_KEY | TUNNEL_CSUM; |
| 256 | if (tb[TCA_TUNNEL_KEY_NO_CSUM] && |
| 257 | nla_get_u8(tb[TCA_TUNNEL_KEY_NO_CSUM])) |
| 258 | flags &= ~TUNNEL_CSUM; |
| 259 | |
Hadar Hen Zion | 75bfbca | 2016-11-07 15:14:41 +0200 | [diff] [blame] | 260 | if (tb[TCA_TUNNEL_KEY_ENC_DST_PORT]) |
| 261 | dst_port = nla_get_be16(tb[TCA_TUNNEL_KEY_ENC_DST_PORT]); |
| 262 | |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 263 | if (tb[TCA_TUNNEL_KEY_ENC_OPTS]) { |
| 264 | opts_len = tunnel_key_get_opts_len(tb[TCA_TUNNEL_KEY_ENC_OPTS], |
| 265 | extack); |
| 266 | if (opts_len < 0) { |
| 267 | ret = opts_len; |
| 268 | goto err_out; |
| 269 | } |
| 270 | } |
| 271 | |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 272 | if (tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC] && |
| 273 | tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]) { |
| 274 | __be32 saddr; |
| 275 | __be32 daddr; |
| 276 | |
| 277 | saddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_SRC]); |
| 278 | daddr = nla_get_in_addr(tb[TCA_TUNNEL_KEY_ENC_IPV4_DST]); |
| 279 | |
| 280 | metadata = __ip_tun_set_dst(saddr, daddr, 0, 0, |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 281 | dst_port, flags, |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 282 | key_id, opts_len); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 283 | } else if (tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC] && |
| 284 | tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]) { |
| 285 | struct in6_addr saddr; |
| 286 | struct in6_addr daddr; |
| 287 | |
| 288 | saddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_SRC]); |
| 289 | daddr = nla_get_in6_addr(tb[TCA_TUNNEL_KEY_ENC_IPV6_DST]); |
| 290 | |
Or Gerlitz | dc594ec | 2016-12-22 14:28:14 +0200 | [diff] [blame] | 291 | metadata = __ipv6_tun_set_dst(&saddr, &daddr, 0, 0, dst_port, |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 292 | 0, flags, |
Hadar Hen Zion | 75bfbca | 2016-11-07 15:14:41 +0200 | [diff] [blame] | 293 | key_id, 0); |
Simon Horman | a1165b5 | 2018-06-26 21:39:34 -0700 | [diff] [blame] | 294 | } else { |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 295 | NL_SET_ERR_MSG(extack, "Missing either ipv4 or ipv6 src and dst"); |
Simon Horman | a1165b5 | 2018-06-26 21:39:34 -0700 | [diff] [blame] | 296 | ret = -EINVAL; |
| 297 | goto err_out; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 298 | } |
| 299 | |
| 300 | if (!metadata) { |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 301 | NL_SET_ERR_MSG(extack, "Cannot allocate tunnel metadata dst"); |
Simon Horman | a1165b5 | 2018-06-26 21:39:34 -0700 | [diff] [blame] | 302 | ret = -ENOMEM; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 303 | goto err_out; |
| 304 | } |
| 305 | |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 306 | if (opts_len) { |
| 307 | ret = tunnel_key_opts_set(tb[TCA_TUNNEL_KEY_ENC_OPTS], |
| 308 | &metadata->u.tun_info, |
| 309 | opts_len, extack); |
| 310 | if (ret < 0) |
| 311 | goto err_out; |
| 312 | } |
| 313 | |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 314 | metadata->u.tun_info.mode |= IP_TUNNEL_INFO_TX; |
| 315 | break; |
| 316 | default: |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 317 | NL_SET_ERR_MSG(extack, "Unknown tunnel key action"); |
Roman Mashak | 51d4740 | 2018-03-12 16:20:58 -0400 | [diff] [blame] | 318 | ret = -EINVAL; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 319 | goto err_out; |
| 320 | } |
| 321 | |
| 322 | if (!exists) { |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 323 | ret = tcf_idr_create(tn, parm->index, est, a, |
| 324 | &act_tunnel_key_ops, bind, true); |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 325 | if (ret) { |
| 326 | NL_SET_ERR_MSG(extack, "Cannot create TC IDR"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 327 | return ret; |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 328 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 329 | |
| 330 | ret = ACT_P_CREATED; |
| 331 | } else { |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 332 | tcf_idr_release(*a, bind); |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 333 | if (!ovr) { |
| 334 | NL_SET_ERR_MSG(extack, "TC IDR already exists"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 335 | return -EEXIST; |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 336 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 337 | } |
| 338 | |
| 339 | t = to_tunnel_key(*a); |
| 340 | |
| 341 | ASSERT_RTNL(); |
| 342 | params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); |
| 343 | if (unlikely(!params_new)) { |
| 344 | if (ret == ACT_P_CREATED) |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 345 | tcf_idr_release(*a, bind); |
Simon Horman | 9d7298c | 2018-06-26 21:39:35 -0700 | [diff] [blame] | 346 | NL_SET_ERR_MSG(extack, "Cannot allocate tunnel key parameters"); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 347 | return -ENOMEM; |
| 348 | } |
| 349 | |
| 350 | params_old = rtnl_dereference(t->params); |
| 351 | |
| 352 | params_new->action = parm->action; |
| 353 | params_new->tcft_action = parm->t_action; |
| 354 | params_new->tcft_enc_metadata = metadata; |
| 355 | |
| 356 | rcu_assign_pointer(t->params, params_new); |
| 357 | |
| 358 | if (params_old) |
| 359 | kfree_rcu(params_old, rcu); |
| 360 | |
| 361 | if (ret == ACT_P_CREATED) |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 362 | tcf_idr_insert(tn, *a); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 363 | |
| 364 | return ret; |
| 365 | |
| 366 | err_out: |
| 367 | if (exists) |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 368 | tcf_idr_release(*a, bind); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 369 | return ret; |
| 370 | } |
| 371 | |
Cong Wang | 9a63b25 | 2017-12-05 12:53:07 -0800 | [diff] [blame] | 372 | static void tunnel_key_release(struct tc_action *a) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 373 | { |
| 374 | struct tcf_tunnel_key *t = to_tunnel_key(a); |
| 375 | struct tcf_tunnel_key_params *params; |
| 376 | |
Hadar Hen Zion | 07c0f09 | 2016-09-12 15:19:21 +0300 | [diff] [blame] | 377 | params = rcu_dereference_protected(t->params, 1); |
Davide Caratti | abdadd3 | 2018-03-16 00:00:55 +0100 | [diff] [blame] | 378 | if (params) { |
| 379 | if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) |
| 380 | dst_release(¶ms->tcft_enc_metadata->dst); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 381 | |
Davide Caratti | abdadd3 | 2018-03-16 00:00:55 +0100 | [diff] [blame] | 382 | kfree_rcu(params, rcu); |
| 383 | } |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 384 | } |
| 385 | |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 386 | static int tunnel_key_geneve_opts_dump(struct sk_buff *skb, |
| 387 | const struct ip_tunnel_info *info) |
| 388 | { |
| 389 | int len = info->options_len; |
| 390 | u8 *src = (u8 *)(info + 1); |
| 391 | struct nlattr *start; |
| 392 | |
| 393 | start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS_GENEVE); |
| 394 | if (!start) |
| 395 | return -EMSGSIZE; |
| 396 | |
| 397 | while (len > 0) { |
| 398 | struct geneve_opt *opt = (struct geneve_opt *)src; |
| 399 | |
| 400 | if (nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_CLASS, |
| 401 | opt->opt_class) || |
| 402 | nla_put_u8(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_TYPE, |
| 403 | opt->type) || |
| 404 | nla_put(skb, TCA_TUNNEL_KEY_ENC_OPT_GENEVE_DATA, |
| 405 | opt->length * 4, opt + 1)) |
| 406 | return -EMSGSIZE; |
| 407 | |
| 408 | len -= sizeof(struct geneve_opt) + opt->length * 4; |
| 409 | src += sizeof(struct geneve_opt) + opt->length * 4; |
| 410 | } |
| 411 | |
| 412 | nla_nest_end(skb, start); |
| 413 | return 0; |
| 414 | } |
| 415 | |
| 416 | static int tunnel_key_opts_dump(struct sk_buff *skb, |
| 417 | const struct ip_tunnel_info *info) |
| 418 | { |
| 419 | struct nlattr *start; |
| 420 | int err; |
| 421 | |
| 422 | if (!info->options_len) |
| 423 | return 0; |
| 424 | |
| 425 | start = nla_nest_start(skb, TCA_TUNNEL_KEY_ENC_OPTS); |
| 426 | if (!start) |
| 427 | return -EMSGSIZE; |
| 428 | |
| 429 | if (info->key.tun_flags & TUNNEL_GENEVE_OPT) { |
| 430 | err = tunnel_key_geneve_opts_dump(skb, info); |
| 431 | if (err) |
| 432 | return err; |
| 433 | } else { |
| 434 | return -EINVAL; |
| 435 | } |
| 436 | |
| 437 | nla_nest_end(skb, start); |
| 438 | return 0; |
| 439 | } |
| 440 | |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 441 | static int tunnel_key_dump_addresses(struct sk_buff *skb, |
| 442 | const struct ip_tunnel_info *info) |
| 443 | { |
| 444 | unsigned short family = ip_tunnel_info_af(info); |
| 445 | |
| 446 | if (family == AF_INET) { |
| 447 | __be32 saddr = info->key.u.ipv4.src; |
| 448 | __be32 daddr = info->key.u.ipv4.dst; |
| 449 | |
| 450 | if (!nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_SRC, saddr) && |
| 451 | !nla_put_in_addr(skb, TCA_TUNNEL_KEY_ENC_IPV4_DST, daddr)) |
| 452 | return 0; |
| 453 | } |
| 454 | |
| 455 | if (family == AF_INET6) { |
| 456 | const struct in6_addr *saddr6 = &info->key.u.ipv6.src; |
| 457 | const struct in6_addr *daddr6 = &info->key.u.ipv6.dst; |
| 458 | |
| 459 | if (!nla_put_in6_addr(skb, |
| 460 | TCA_TUNNEL_KEY_ENC_IPV6_SRC, saddr6) && |
| 461 | !nla_put_in6_addr(skb, |
| 462 | TCA_TUNNEL_KEY_ENC_IPV6_DST, daddr6)) |
| 463 | return 0; |
| 464 | } |
| 465 | |
| 466 | return -EINVAL; |
| 467 | } |
| 468 | |
| 469 | static int tunnel_key_dump(struct sk_buff *skb, struct tc_action *a, |
| 470 | int bind, int ref) |
| 471 | { |
| 472 | unsigned char *b = skb_tail_pointer(skb); |
| 473 | struct tcf_tunnel_key *t = to_tunnel_key(a); |
| 474 | struct tcf_tunnel_key_params *params; |
| 475 | struct tc_tunnel_key opt = { |
| 476 | .index = t->tcf_index, |
| 477 | .refcnt = t->tcf_refcnt - ref, |
| 478 | .bindcnt = t->tcf_bindcnt - bind, |
| 479 | }; |
| 480 | struct tcf_t tm; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 481 | |
Hadar Hen Zion | 07c0f09 | 2016-09-12 15:19:21 +0300 | [diff] [blame] | 482 | params = rtnl_dereference(t->params); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 483 | |
| 484 | opt.t_action = params->tcft_action; |
| 485 | opt.action = params->action; |
| 486 | |
| 487 | if (nla_put(skb, TCA_TUNNEL_KEY_PARMS, sizeof(opt), &opt)) |
| 488 | goto nla_put_failure; |
| 489 | |
| 490 | if (params->tcft_action == TCA_TUNNEL_KEY_ACT_SET) { |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 491 | struct ip_tunnel_info *info = |
| 492 | ¶ms->tcft_enc_metadata->u.tun_info; |
| 493 | struct ip_tunnel_key *key = &info->key; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 494 | __be32 key_id = tunnel_id_to_key32(key->tun_id); |
| 495 | |
| 496 | if (nla_put_be32(skb, TCA_TUNNEL_KEY_ENC_KEY_ID, key_id) || |
| 497 | tunnel_key_dump_addresses(skb, |
Hadar Hen Zion | 75bfbca | 2016-11-07 15:14:41 +0200 | [diff] [blame] | 498 | ¶ms->tcft_enc_metadata->u.tun_info) || |
Jiri Benc | 86087e1 | 2017-06-14 21:19:31 +0200 | [diff] [blame] | 499 | nla_put_be16(skb, TCA_TUNNEL_KEY_ENC_DST_PORT, key->tp_dst) || |
| 500 | nla_put_u8(skb, TCA_TUNNEL_KEY_NO_CSUM, |
Simon Horman | 0ed5269 | 2018-06-26 21:39:37 -0700 | [diff] [blame^] | 501 | !(key->tun_flags & TUNNEL_CSUM)) || |
| 502 | tunnel_key_opts_dump(skb, info)) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 503 | goto nla_put_failure; |
| 504 | } |
| 505 | |
| 506 | tcf_tm_dump(&tm, &t->tcf_tm); |
| 507 | if (nla_put_64bit(skb, TCA_TUNNEL_KEY_TM, sizeof(tm), |
| 508 | &tm, TCA_TUNNEL_KEY_PAD)) |
| 509 | goto nla_put_failure; |
| 510 | |
Hadar Hen Zion | 07c0f09 | 2016-09-12 15:19:21 +0300 | [diff] [blame] | 511 | return skb->len; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 512 | |
| 513 | nla_put_failure: |
| 514 | nlmsg_trim(skb, b); |
Hadar Hen Zion | 07c0f09 | 2016-09-12 15:19:21 +0300 | [diff] [blame] | 515 | return -1; |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 516 | } |
| 517 | |
| 518 | static int tunnel_key_walker(struct net *net, struct sk_buff *skb, |
| 519 | struct netlink_callback *cb, int type, |
Alexander Aring | 4178010 | 2018-02-15 10:54:58 -0500 | [diff] [blame] | 520 | const struct tc_action_ops *ops, |
| 521 | struct netlink_ext_ack *extack) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 522 | { |
| 523 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); |
| 524 | |
Alexander Aring | b362014 | 2018-02-15 10:54:59 -0500 | [diff] [blame] | 525 | return tcf_generic_walker(tn, skb, cb, type, ops, extack); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 526 | } |
| 527 | |
Alexander Aring | 331a929 | 2018-02-15 10:54:57 -0500 | [diff] [blame] | 528 | static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index, |
| 529 | struct netlink_ext_ack *extack) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 530 | { |
| 531 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); |
| 532 | |
Chris Mi | 65a206c | 2017-08-30 02:31:59 -0400 | [diff] [blame] | 533 | return tcf_idr_search(tn, a, index); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 534 | } |
| 535 | |
| 536 | static struct tc_action_ops act_tunnel_key_ops = { |
| 537 | .kind = "tunnel_key", |
| 538 | .type = TCA_ACT_TUNNEL_KEY, |
| 539 | .owner = THIS_MODULE, |
| 540 | .act = tunnel_key_act, |
| 541 | .dump = tunnel_key_dump, |
| 542 | .init = tunnel_key_init, |
| 543 | .cleanup = tunnel_key_release, |
| 544 | .walk = tunnel_key_walker, |
| 545 | .lookup = tunnel_key_search, |
| 546 | .size = sizeof(struct tcf_tunnel_key), |
| 547 | }; |
| 548 | |
| 549 | static __net_init int tunnel_key_init_net(struct net *net) |
| 550 | { |
| 551 | struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); |
| 552 | |
Cong Wang | c7e460c | 2017-11-06 13:47:18 -0800 | [diff] [blame] | 553 | return tc_action_net_init(tn, &act_tunnel_key_ops); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 554 | } |
| 555 | |
Cong Wang | 039af9c | 2017-12-11 15:35:03 -0800 | [diff] [blame] | 556 | static void __net_exit tunnel_key_exit_net(struct list_head *net_list) |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 557 | { |
Cong Wang | 039af9c | 2017-12-11 15:35:03 -0800 | [diff] [blame] | 558 | tc_action_net_exit(net_list, tunnel_key_net_id); |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 559 | } |
| 560 | |
| 561 | static struct pernet_operations tunnel_key_net_ops = { |
| 562 | .init = tunnel_key_init_net, |
Cong Wang | 039af9c | 2017-12-11 15:35:03 -0800 | [diff] [blame] | 563 | .exit_batch = tunnel_key_exit_net, |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 564 | .id = &tunnel_key_net_id, |
| 565 | .size = sizeof(struct tc_action_net), |
Amir Vadai | d0f6dd8 | 2016-09-08 16:23:48 +0300 | [diff] [blame] | 566 | }; |
| 567 | |
| 568 | static int __init tunnel_key_init_module(void) |
| 569 | { |
| 570 | return tcf_register_action(&act_tunnel_key_ops, &tunnel_key_net_ops); |
| 571 | } |
| 572 | |
| 573 | static void __exit tunnel_key_cleanup_module(void) |
| 574 | { |
| 575 | tcf_unregister_action(&act_tunnel_key_ops, &tunnel_key_net_ops); |
| 576 | } |
| 577 | |
| 578 | module_init(tunnel_key_init_module); |
| 579 | module_exit(tunnel_key_cleanup_module); |
| 580 | |
| 581 | MODULE_AUTHOR("Amir Vadai <amir@vadai.me>"); |
| 582 | MODULE_DESCRIPTION("ip tunnel manipulation actions"); |
| 583 | MODULE_LICENSE("GPL v2"); |