Skip to content

Commit 2f1d370

Browse files
lxindavem330
authored andcommitted
lwtunnel: add support for multiple geneve opts
geneve RFC (draft-ietf-nvo3-geneve-14) allows a geneve packet to carry multiple geneve opts, so it's necessary for lwtunnel to support adding multiple geneve opts in one lwtunnel route. But vxlan and erspan opts are still only allowed to add one option. With this patch, iproute2 could make it like: # ip r a 1.1.1.0/24 encap ip id 1 geneve_opts 0:0:12121212,1:2:12121212 \ dst 10.1.0.2 dev geneve1 # ip r a 1.1.1.0/24 encap ip id 1 vxlan_opts 456 \ dst 10.1.0.2 dev erspan1 # ip r a 1.1.1.0/24 encap ip id 1 erspan_opts 1:123:0:0 \ dst 10.1.0.2 dev erspan1 Which are pretty much like cls_flower and act_tunnel_key. Signed-off-by: Xin Long <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 272630f commit 2f1d370

File tree

1 file changed

+75
-36
lines changed

1 file changed

+75
-36
lines changed

net/ipv4/ip_tunnel_core.c

Lines changed: 75 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ erspan_opt_policy[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1] = {
251251
};
252252

253253
static int ip_tun_parse_opts_geneve(struct nlattr *attr,
254-
struct ip_tunnel_info *info,
254+
struct ip_tunnel_info *info, int opts_len,
255255
struct netlink_ext_ack *extack)
256256
{
257257
struct nlattr *tb[LWTUNNEL_IP_OPT_GENEVE_MAX + 1];
@@ -273,7 +273,7 @@ static int ip_tun_parse_opts_geneve(struct nlattr *attr,
273273
return -EINVAL;
274274

275275
if (info) {
276-
struct geneve_opt *opt = ip_tunnel_info_opts(info);
276+
struct geneve_opt *opt = ip_tunnel_info_opts(info) + opts_len;
277277

278278
memcpy(opt->opt_data, nla_data(attr), data_len);
279279
opt->length = data_len / 4;
@@ -288,7 +288,7 @@ static int ip_tun_parse_opts_geneve(struct nlattr *attr,
288288
}
289289

290290
static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
291-
struct ip_tunnel_info *info,
291+
struct ip_tunnel_info *info, int opts_len,
292292
struct netlink_ext_ack *extack)
293293
{
294294
struct nlattr *tb[LWTUNNEL_IP_OPT_VXLAN_MAX + 1];
@@ -303,7 +303,8 @@ static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
303303
return -EINVAL;
304304

305305
if (info) {
306-
struct vxlan_metadata *md = ip_tunnel_info_opts(info);
306+
struct vxlan_metadata *md =
307+
ip_tunnel_info_opts(info) + opts_len;
307308

308309
attr = tb[LWTUNNEL_IP_OPT_VXLAN_GBP];
309310
md->gbp = nla_get_u32(attr);
@@ -314,7 +315,7 @@ static int ip_tun_parse_opts_vxlan(struct nlattr *attr,
314315
}
315316

316317
static int ip_tun_parse_opts_erspan(struct nlattr *attr,
317-
struct ip_tunnel_info *info,
318+
struct ip_tunnel_info *info, int opts_len,
318319
struct netlink_ext_ack *extack)
319320
{
320321
struct nlattr *tb[LWTUNNEL_IP_OPT_ERSPAN_MAX + 1];
@@ -329,7 +330,8 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
329330
return -EINVAL;
330331

331332
if (info) {
332-
struct erspan_metadata *md = ip_tunnel_info_opts(info);
333+
struct erspan_metadata *md =
334+
ip_tunnel_info_opts(info) + opts_len;
333335

334336
attr = tb[LWTUNNEL_IP_OPT_ERSPAN_VER];
335337
md->version = nla_get_u8(attr);
@@ -356,30 +358,57 @@ static int ip_tun_parse_opts_erspan(struct nlattr *attr,
356358
static int ip_tun_parse_opts(struct nlattr *attr, struct ip_tunnel_info *info,
357359
struct netlink_ext_ack *extack)
358360
{
359-
struct nlattr *tb[LWTUNNEL_IP_OPTS_MAX + 1];
360-
int err;
361+
int err, rem, opt_len, opts_len = 0, type = 0;
362+
struct nlattr *nla;
361363

362364
if (!attr)
363365
return 0;
364366

365-
err = nla_parse_nested(tb, LWTUNNEL_IP_OPTS_MAX, attr,
366-
ip_opts_policy, extack);
367+
err = nla_validate(nla_data(attr), nla_len(attr), LWTUNNEL_IP_OPTS_MAX,
368+
ip_opts_policy, extack);
367369
if (err)
368370
return err;
369371

370-
if (tb[LWTUNNEL_IP_OPTS_GENEVE])
371-
err = ip_tun_parse_opts_geneve(tb[LWTUNNEL_IP_OPTS_GENEVE],
372-
info, extack);
373-
else if (tb[LWTUNNEL_IP_OPTS_VXLAN])
374-
err = ip_tun_parse_opts_vxlan(tb[LWTUNNEL_IP_OPTS_VXLAN],
375-
info, extack);
376-
else if (tb[LWTUNNEL_IP_OPTS_ERSPAN])
377-
err = ip_tun_parse_opts_erspan(tb[LWTUNNEL_IP_OPTS_ERSPAN],
378-
info, extack);
379-
else
380-
err = -EINVAL;
372+
nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
373+
switch (nla_type(nla)) {
374+
case LWTUNNEL_IP_OPTS_GENEVE:
375+
if (type && type != TUNNEL_GENEVE_OPT)
376+
return -EINVAL;
377+
opt_len = ip_tun_parse_opts_geneve(nla, info, opts_len,
378+
extack);
379+
if (opt_len < 0)
380+
return opt_len;
381+
opts_len += opt_len;
382+
if (opts_len > IP_TUNNEL_OPTS_MAX)
383+
return -EINVAL;
384+
type = TUNNEL_GENEVE_OPT;
385+
break;
386+
case LWTUNNEL_IP_OPTS_VXLAN:
387+
if (type)
388+
return -EINVAL;
389+
opt_len = ip_tun_parse_opts_vxlan(nla, info, opts_len,
390+
extack);
391+
if (opt_len < 0)
392+
return opt_len;
393+
opts_len += opt_len;
394+
type = TUNNEL_VXLAN_OPT;
395+
break;
396+
case LWTUNNEL_IP_OPTS_ERSPAN:
397+
if (type)
398+
return -EINVAL;
399+
opt_len = ip_tun_parse_opts_erspan(nla, info, opts_len,
400+
extack);
401+
if (opt_len < 0)
402+
return opt_len;
403+
opts_len += opt_len;
404+
type = TUNNEL_ERSPAN_OPT;
405+
break;
406+
default:
407+
return -EINVAL;
408+
}
409+
}
381410

382-
return err;
411+
return opts_len;
383412
}
384413

385414
static int ip_tun_get_optlen(struct nlattr *attr,
@@ -477,18 +506,23 @@ static int ip_tun_fill_encap_opts_geneve(struct sk_buff *skb,
477506
{
478507
struct geneve_opt *opt;
479508
struct nlattr *nest;
509+
int offset = 0;
480510

481511
nest = nla_nest_start_noflag(skb, LWTUNNEL_IP_OPTS_GENEVE);
482512
if (!nest)
483513
return -ENOMEM;
484514

485-
opt = ip_tunnel_info_opts(tun_info);
486-
if (nla_put_be16(skb, LWTUNNEL_IP_OPT_GENEVE_CLASS, opt->opt_class) ||
487-
nla_put_u8(skb, LWTUNNEL_IP_OPT_GENEVE_TYPE, opt->type) ||
488-
nla_put(skb, LWTUNNEL_IP_OPT_GENEVE_DATA, opt->length * 4,
489-
opt->opt_data)) {
490-
nla_nest_cancel(skb, nest);
491-
return -ENOMEM;
515+
while (tun_info->options_len > offset) {
516+
opt = ip_tunnel_info_opts(tun_info) + offset;
517+
if (nla_put_be16(skb, LWTUNNEL_IP_OPT_GENEVE_CLASS,
518+
opt->opt_class) ||
519+
nla_put_u8(skb, LWTUNNEL_IP_OPT_GENEVE_TYPE, opt->type) ||
520+
nla_put(skb, LWTUNNEL_IP_OPT_GENEVE_DATA, opt->length * 4,
521+
opt->opt_data)) {
522+
nla_nest_cancel(skb, nest);
523+
return -ENOMEM;
524+
}
525+
offset += sizeof(*opt) + opt->length * 4;
492526
}
493527

494528
nla_nest_end(skb, nest);
@@ -602,13 +636,18 @@ static int ip_tun_opts_nlsize(struct ip_tunnel_info *info)
602636

603637
opt_len = nla_total_size(0); /* LWTUNNEL_IP_OPTS */
604638
if (info->key.tun_flags & TUNNEL_GENEVE_OPT) {
605-
struct geneve_opt *opt = ip_tunnel_info_opts(info);
606-
607-
opt_len += nla_total_size(0) /* LWTUNNEL_IP_OPTS_GENEVE */
608-
+ nla_total_size(2) /* OPT_GENEVE_CLASS */
609-
+ nla_total_size(1) /* OPT_GENEVE_TYPE */
610-
+ nla_total_size(opt->length * 4);
611-
/* OPT_GENEVE_DATA */
639+
struct geneve_opt *opt;
640+
int offset = 0;
641+
642+
opt_len += nla_total_size(0); /* LWTUNNEL_IP_OPTS_GENEVE */
643+
while (info->options_len > offset) {
644+
opt = ip_tunnel_info_opts(info) + offset;
645+
opt_len += nla_total_size(2) /* OPT_GENEVE_CLASS */
646+
+ nla_total_size(1) /* OPT_GENEVE_TYPE */
647+
+ nla_total_size(opt->length * 4);
648+
/* OPT_GENEVE_DATA */
649+
offset += sizeof(*opt) + opt->length * 4;
650+
}
612651
} else if (info->key.tun_flags & TUNNEL_VXLAN_OPT) {
613652
opt_len += nla_total_size(0) /* LWTUNNEL_IP_OPTS_VXLAN */
614653
+ nla_total_size(4); /* OPT_VXLAN_GBP */

0 commit comments

Comments
 (0)