Skip to content

Commit b2d0f5d

Browse files
yi-y-yangdavem330
authored andcommitted
openvswitch: enable NSH support
v16->17 - Fixed disputed check code: keep them in nsh_push and nsh_pop but also add them in __ovs_nla_copy_actions v15->v16 - Add csum recalculation for nsh_push, nsh_pop and set_nsh pointed out by Pravin - Move nsh key into the union with ipv4 and ipv6 and add check for nsh key in match_validate pointed out by Pravin - Add nsh check in validate_set and __ovs_nla_copy_actions v14->v15 - Check size in nsh_hdr_from_nlattr - Fixed four small issues pointed out By Jiri and Eric v13->v14 - Rename skb_push_nsh to nsh_push per Dave's comment - Rename skb_pop_nsh to nsh_pop per Dave's comment v12->v13 - Fix NSH header length check in set_nsh v11->v12 - Fix missing changes old comments pointed out - Fix new comments for v11 v10->v11 - Fix the left three disputable comments for v9 but not fixed in v10. v9->v10 - Change struct ovs_key_nsh to struct ovs_nsh_key_base base; __be32 context[NSH_MD1_CONTEXT_SIZE]; - Fix new comments for v9 v8->v9 - Fix build error reported by daily intel build because nsh module isn't selected by openvswitch v7->v8 - Rework nested value and mask for OVS_KEY_ATTR_NSH - Change pop_nsh to adapt to nsh kernel module - Fix many issues per comments from Jiri Benc v6->v7 - Remove NSH GSO patches in v6 because Jiri Benc reworked it as another patch series and they have been merged. - Change it to adapt to nsh kernel module added by NSH GSO patch series v5->v6 - Fix the rest comments for v4. - Add NSH GSO support for VxLAN-gpe + NSH and Eth + NSH. v4->v5 - Fix many comments by Jiri Benc and Eric Garver for v4. v3->v4 - Add new NSH match field ttl - Update NSH header to the latest format which will be final format and won't change per its author's confirmation. - Fix comments for v3. v2->v3 - Change OVS_KEY_ATTR_NSH to nested key to handle length-fixed attributes and length-variable attriubte more flexibly. - Remove struct ovs_action_push_nsh completely - Add code to handle nested attribute for SET_MASKED - Change PUSH_NSH to use the nested OVS_KEY_ATTR_NSH to transfer NSH header data. - Fix comments and coding style issues by Jiri and Eric v1->v2 - Change encap_nsh and decap_nsh to push_nsh and pop_nsh - Dynamically allocate struct ovs_action_push_nsh for length-variable metadata. OVS master and 2.8 branch has merged NSH userspace patch series, this patch is to enable NSH support in kernel data path in order that OVS can support NSH in compat mode by porting this. Signed-off-by: Yi Yang <[email protected]> Acked-by: Jiri Benc <[email protected]> Acked-by: Eric Garver <[email protected]> Acked-by: Pravin Shelar <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 7f5d3f2 commit b2d0f5d

File tree

9 files changed

+613
-2
lines changed

9 files changed

+613
-2
lines changed

include/net/nsh.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,4 +304,7 @@ static inline void nsh_set_flags_ttl_len(struct nshhdr *nsh, u8 flags,
304304
NSH_FLAGS_MASK | NSH_TTL_MASK | NSH_LEN_MASK);
305305
}
306306

307+
int nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh);
308+
int nsh_pop(struct sk_buff *skb);
309+
307310
#endif /* __NET_NSH_H */

include/uapi/linux/openvswitch.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@ enum ovs_key_attr {
336336
OVS_KEY_ATTR_CT_LABELS, /* 16-octet connection tracking label */
337337
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, /* struct ovs_key_ct_tuple_ipv4 */
338338
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */
339+
OVS_KEY_ATTR_NSH, /* Nested set of ovs_nsh_key_* */
339340

340341
#ifdef __KERNEL__
341342
OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */
@@ -495,6 +496,30 @@ struct ovs_key_ct_tuple_ipv6 {
495496
__u8 ipv6_proto;
496497
};
497498

499+
enum ovs_nsh_key_attr {
500+
OVS_NSH_KEY_ATTR_UNSPEC,
501+
OVS_NSH_KEY_ATTR_BASE, /* struct ovs_nsh_key_base. */
502+
OVS_NSH_KEY_ATTR_MD1, /* struct ovs_nsh_key_md1. */
503+
OVS_NSH_KEY_ATTR_MD2, /* variable-length octets for MD type 2. */
504+
__OVS_NSH_KEY_ATTR_MAX
505+
};
506+
507+
#define OVS_NSH_KEY_ATTR_MAX (__OVS_NSH_KEY_ATTR_MAX - 1)
508+
509+
struct ovs_nsh_key_base {
510+
__u8 flags;
511+
__u8 ttl;
512+
__u8 mdtype;
513+
__u8 np;
514+
__be32 path_hdr;
515+
};
516+
517+
#define NSH_MD1_CONTEXT_SIZE 4
518+
519+
struct ovs_nsh_key_md1 {
520+
__be32 context[NSH_MD1_CONTEXT_SIZE];
521+
};
522+
498523
/**
499524
* enum ovs_flow_attr - attributes for %OVS_FLOW_* commands.
500525
* @OVS_FLOW_ATTR_KEY: Nested %OVS_KEY_ATTR_* attributes specifying the flow
@@ -811,6 +836,8 @@ struct ovs_action_push_eth {
811836
* @OVS_ACTION_ATTR_POP_ETH: Pop the outermost Ethernet header off the
812837
* packet.
813838
* @OVS_ACTION_ATTR_CT_CLEAR: Clear conntrack state from the packet.
839+
* @OVS_ACTION_ATTR_PUSH_NSH: push NSH header to the packet.
840+
* @OVS_ACTION_ATTR_POP_NSH: pop the outermost NSH header off the packet.
814841
*
815842
* Only a single header can be set with a single %OVS_ACTION_ATTR_SET. Not all
816843
* fields within a header are modifiable, e.g. the IPv4 protocol and fragment
@@ -841,6 +868,8 @@ enum ovs_action_attr {
841868
OVS_ACTION_ATTR_PUSH_ETH, /* struct ovs_action_push_eth. */
842869
OVS_ACTION_ATTR_POP_ETH, /* No argument. */
843870
OVS_ACTION_ATTR_CT_CLEAR, /* No argument. */
871+
OVS_ACTION_ATTR_PUSH_NSH, /* Nested OVS_NSH_KEY_ATTR_*. */
872+
OVS_ACTION_ATTR_POP_NSH, /* No argument. */
844873

845874
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
846875
* from userspace. */

net/nsh/nsh.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,66 @@
1414
#include <net/nsh.h>
1515
#include <net/tun_proto.h>
1616

17+
int nsh_push(struct sk_buff *skb, const struct nshhdr *pushed_nh)
18+
{
19+
struct nshhdr *nh;
20+
size_t length = nsh_hdr_len(pushed_nh);
21+
u8 next_proto;
22+
23+
if (skb->mac_len) {
24+
next_proto = TUN_P_ETHERNET;
25+
} else {
26+
next_proto = tun_p_from_eth_p(skb->protocol);
27+
if (!next_proto)
28+
return -EAFNOSUPPORT;
29+
}
30+
31+
/* Add the NSH header */
32+
if (skb_cow_head(skb, length) < 0)
33+
return -ENOMEM;
34+
35+
skb_push(skb, length);
36+
nh = (struct nshhdr *)(skb->data);
37+
memcpy(nh, pushed_nh, length);
38+
nh->np = next_proto;
39+
skb_postpush_rcsum(skb, nh, length);
40+
41+
skb->protocol = htons(ETH_P_NSH);
42+
skb_reset_mac_header(skb);
43+
skb_reset_network_header(skb);
44+
skb_reset_mac_len(skb);
45+
46+
return 0;
47+
}
48+
EXPORT_SYMBOL_GPL(nsh_push);
49+
50+
int nsh_pop(struct sk_buff *skb)
51+
{
52+
struct nshhdr *nh;
53+
size_t length;
54+
__be16 inner_proto;
55+
56+
if (!pskb_may_pull(skb, NSH_BASE_HDR_LEN))
57+
return -ENOMEM;
58+
nh = (struct nshhdr *)(skb->data);
59+
length = nsh_hdr_len(nh);
60+
inner_proto = tun_p_to_eth_p(nh->np);
61+
if (!pskb_may_pull(skb, length))
62+
return -ENOMEM;
63+
64+
if (!inner_proto)
65+
return -EAFNOSUPPORT;
66+
67+
skb_pull_rcsum(skb, length);
68+
skb_reset_mac_header(skb);
69+
skb_reset_network_header(skb);
70+
skb_reset_mac_len(skb);
71+
skb->protocol = inner_proto;
72+
73+
return 0;
74+
}
75+
EXPORT_SYMBOL_GPL(nsh_pop);
76+
1777
static struct sk_buff *nsh_gso_segment(struct sk_buff *skb,
1878
netdev_features_t features)
1979
{

net/openvswitch/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ config OPENVSWITCH
1414
select MPLS
1515
select NET_MPLS_GSO
1616
select DST_CACHE
17+
select NET_NSH
1718
---help---
1819
Open vSwitch is a multilayer Ethernet switch targeted at virtualized
1920
environments. In addition to supporting a variety of features

net/openvswitch/actions.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#include "flow.h"
4444
#include "conntrack.h"
4545
#include "vport.h"
46+
#include "flow_netlink.h"
4647

4748
struct deferred_action {
4849
struct sk_buff *skb;
@@ -380,6 +381,38 @@ static int push_eth(struct sk_buff *skb, struct sw_flow_key *key,
380381
return 0;
381382
}
382383

384+
static int push_nsh(struct sk_buff *skb, struct sw_flow_key *key,
385+
const struct nshhdr *nh)
386+
{
387+
int err;
388+
389+
err = nsh_push(skb, nh);
390+
if (err)
391+
return err;
392+
393+
/* safe right before invalidate_flow_key */
394+
key->mac_proto = MAC_PROTO_NONE;
395+
invalidate_flow_key(key);
396+
return 0;
397+
}
398+
399+
static int pop_nsh(struct sk_buff *skb, struct sw_flow_key *key)
400+
{
401+
int err;
402+
403+
err = nsh_pop(skb);
404+
if (err)
405+
return err;
406+
407+
/* safe right before invalidate_flow_key */
408+
if (skb->protocol == htons(ETH_P_TEB))
409+
key->mac_proto = MAC_PROTO_ETHERNET;
410+
else
411+
key->mac_proto = MAC_PROTO_NONE;
412+
invalidate_flow_key(key);
413+
return 0;
414+
}
415+
383416
static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh,
384417
__be32 addr, __be32 new_addr)
385418
{
@@ -602,6 +635,69 @@ static int set_ipv6(struct sk_buff *skb, struct sw_flow_key *flow_key,
602635
return 0;
603636
}
604637

638+
static int set_nsh(struct sk_buff *skb, struct sw_flow_key *flow_key,
639+
const struct nlattr *a)
640+
{
641+
struct nshhdr *nh;
642+
size_t length;
643+
int err;
644+
u8 flags;
645+
u8 ttl;
646+
int i;
647+
648+
struct ovs_key_nsh key;
649+
struct ovs_key_nsh mask;
650+
651+
err = nsh_key_from_nlattr(a, &key, &mask);
652+
if (err)
653+
return err;
654+
655+
/* Make sure the NSH base header is there */
656+
if (!pskb_may_pull(skb, skb_network_offset(skb) + NSH_BASE_HDR_LEN))
657+
return -ENOMEM;
658+
659+
nh = nsh_hdr(skb);
660+
length = nsh_hdr_len(nh);
661+
662+
/* Make sure the whole NSH header is there */
663+
err = skb_ensure_writable(skb, skb_network_offset(skb) +
664+
length);
665+
if (unlikely(err))
666+
return err;
667+
668+
nh = nsh_hdr(skb);
669+
skb_postpull_rcsum(skb, nh, length);
670+
flags = nsh_get_flags(nh);
671+
flags = OVS_MASKED(flags, key.base.flags, mask.base.flags);
672+
flow_key->nsh.base.flags = flags;
673+
ttl = nsh_get_ttl(nh);
674+
ttl = OVS_MASKED(ttl, key.base.ttl, mask.base.ttl);
675+
flow_key->nsh.base.ttl = ttl;
676+
nsh_set_flags_and_ttl(nh, flags, ttl);
677+
nh->path_hdr = OVS_MASKED(nh->path_hdr, key.base.path_hdr,
678+
mask.base.path_hdr);
679+
flow_key->nsh.base.path_hdr = nh->path_hdr;
680+
switch (nh->mdtype) {
681+
case NSH_M_TYPE1:
682+
for (i = 0; i < NSH_MD1_CONTEXT_SIZE; i++) {
683+
nh->md1.context[i] =
684+
OVS_MASKED(nh->md1.context[i], key.context[i],
685+
mask.context[i]);
686+
}
687+
memcpy(flow_key->nsh.context, nh->md1.context,
688+
sizeof(nh->md1.context));
689+
break;
690+
case NSH_M_TYPE2:
691+
memset(flow_key->nsh.context, 0,
692+
sizeof(flow_key->nsh.context));
693+
break;
694+
default:
695+
return -EINVAL;
696+
}
697+
skb_postpush_rcsum(skb, nh, length);
698+
return 0;
699+
}
700+
605701
/* Must follow skb_ensure_writable() since that can move the skb data. */
606702
static void set_tp_port(struct sk_buff *skb, __be16 *port,
607703
__be16 new_port, __sum16 *check)
@@ -1024,6 +1120,10 @@ static int execute_masked_set_action(struct sk_buff *skb,
10241120
get_mask(a, struct ovs_key_ethernet *));
10251121
break;
10261122

1123+
case OVS_KEY_ATTR_NSH:
1124+
err = set_nsh(skb, flow_key, a);
1125+
break;
1126+
10271127
case OVS_KEY_ATTR_IPV4:
10281128
err = set_ipv4(skb, flow_key, nla_data(a),
10291129
get_mask(a, struct ovs_key_ipv4 *));
@@ -1214,6 +1314,22 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
12141314
case OVS_ACTION_ATTR_POP_ETH:
12151315
err = pop_eth(skb, key);
12161316
break;
1317+
1318+
case OVS_ACTION_ATTR_PUSH_NSH: {
1319+
u8 buffer[NSH_HDR_MAX_LEN];
1320+
struct nshhdr *nh = (struct nshhdr *)buffer;
1321+
1322+
err = nsh_hdr_from_nlattr(nla_data(a), nh,
1323+
NSH_HDR_MAX_LEN);
1324+
if (unlikely(err))
1325+
break;
1326+
err = push_nsh(skb, key, nh);
1327+
break;
1328+
}
1329+
1330+
case OVS_ACTION_ATTR_POP_NSH:
1331+
err = pop_nsh(skb, key);
1332+
break;
12171333
}
12181334

12191335
if (unlikely(err)) {

net/openvswitch/flow.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <net/ipv6.h>
4747
#include <net/mpls.h>
4848
#include <net/ndisc.h>
49+
#include <net/nsh.h>
4950

5051
#include "conntrack.h"
5152
#include "datapath.h"
@@ -490,6 +491,52 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
490491
return 0;
491492
}
492493

494+
static int parse_nsh(struct sk_buff *skb, struct sw_flow_key *key)
495+
{
496+
struct nshhdr *nh;
497+
unsigned int nh_ofs = skb_network_offset(skb);
498+
u8 version, length;
499+
int err;
500+
501+
err = check_header(skb, nh_ofs + NSH_BASE_HDR_LEN);
502+
if (unlikely(err))
503+
return err;
504+
505+
nh = nsh_hdr(skb);
506+
version = nsh_get_ver(nh);
507+
length = nsh_hdr_len(nh);
508+
509+
if (version != 0)
510+
return -EINVAL;
511+
512+
err = check_header(skb, nh_ofs + length);
513+
if (unlikely(err))
514+
return err;
515+
516+
nh = nsh_hdr(skb);
517+
key->nsh.base.flags = nsh_get_flags(nh);
518+
key->nsh.base.ttl = nsh_get_ttl(nh);
519+
key->nsh.base.mdtype = nh->mdtype;
520+
key->nsh.base.np = nh->np;
521+
key->nsh.base.path_hdr = nh->path_hdr;
522+
switch (key->nsh.base.mdtype) {
523+
case NSH_M_TYPE1:
524+
if (length != NSH_M_TYPE1_LEN)
525+
return -EINVAL;
526+
memcpy(key->nsh.context, nh->md1.context,
527+
sizeof(nh->md1));
528+
break;
529+
case NSH_M_TYPE2:
530+
memset(key->nsh.context, 0,
531+
sizeof(nh->md1));
532+
break;
533+
default:
534+
return -EINVAL;
535+
}
536+
537+
return 0;
538+
}
539+
493540
/**
494541
* key_extract - extracts a flow key from an Ethernet frame.
495542
* @skb: sk_buff that contains the frame, with skb->data pointing to the
@@ -735,6 +782,10 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
735782
memset(&key->tp, 0, sizeof(key->tp));
736783
}
737784
}
785+
} else if (key->eth.type == htons(ETH_P_NSH)) {
786+
error = parse_nsh(skb, key);
787+
if (error)
788+
return error;
738789
}
739790
return 0;
740791
}

net/openvswitch/flow.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include <net/inet_ecn.h>
3636
#include <net/ip_tunnels.h>
3737
#include <net/dst_metadata.h>
38+
#include <net/nsh.h>
3839

3940
struct sk_buff;
4041

@@ -66,6 +67,11 @@ struct vlan_head {
6667
(offsetof(struct sw_flow_key, recirc_id) + \
6768
FIELD_SIZEOF(struct sw_flow_key, recirc_id))
6869

70+
struct ovs_key_nsh {
71+
struct ovs_nsh_key_base base;
72+
__be32 context[NSH_MD1_CONTEXT_SIZE];
73+
};
74+
6975
struct sw_flow_key {
7076
u8 tun_opts[IP_TUNNEL_OPTS_MAX];
7177
u8 tun_opts_len;
@@ -143,6 +149,7 @@ struct sw_flow_key {
143149
} nd;
144150
};
145151
} ipv6;
152+
struct ovs_key_nsh nsh; /* network service header */
146153
};
147154
struct {
148155
/* Connection tracking fields not packed above. */

0 commit comments

Comments
 (0)