mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-26 14:17:26 -04:00
net: openvswitch: IPv6: Add IPv6 extension header support
This change adds a new OpenFlow field OFPXMT_OFB_IPV6_EXTHDR and packets can be filtered using ipv6_ext flag. Signed-off-by: Toms Atteka <cpp.code.lv@gmail.com> Acked-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
a46e3d5eb7
commit
28a3f06017
4 changed files with 184 additions and 2 deletions
|
@ -351,6 +351,7 @@ enum ovs_key_attr {
|
||||||
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, /* struct ovs_key_ct_tuple_ipv4 */
|
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV4, /* struct ovs_key_ct_tuple_ipv4 */
|
||||||
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */
|
OVS_KEY_ATTR_CT_ORIG_TUPLE_IPV6, /* struct ovs_key_ct_tuple_ipv6 */
|
||||||
OVS_KEY_ATTR_NSH, /* Nested set of ovs_nsh_key_* */
|
OVS_KEY_ATTR_NSH, /* Nested set of ovs_nsh_key_* */
|
||||||
|
OVS_KEY_ATTR_IPV6_EXTHDRS, /* struct ovs_key_ipv6_exthdr */
|
||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */
|
OVS_KEY_ATTR_TUNNEL_INFO, /* struct ip_tunnel_info */
|
||||||
|
@ -430,6 +431,11 @@ struct ovs_key_ipv6 {
|
||||||
__u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */
|
__u8 ipv6_frag; /* One of OVS_FRAG_TYPE_*. */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* separate structure to support backward compatibility with older user space */
|
||||||
|
struct ovs_key_ipv6_exthdrs {
|
||||||
|
__u16 hdrs;
|
||||||
|
};
|
||||||
|
|
||||||
struct ovs_key_tcp {
|
struct ovs_key_tcp {
|
||||||
__be16 tcp_src;
|
__be16 tcp_src;
|
||||||
__be16 tcp_dst;
|
__be16 tcp_dst;
|
||||||
|
|
|
@ -241,6 +241,144 @@ static bool icmphdr_ok(struct sk_buff *skb)
|
||||||
sizeof(struct icmphdr));
|
sizeof(struct icmphdr));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_ipv6_ext_hdrs() - Parses packet and sets IPv6 extension header flags.
|
||||||
|
*
|
||||||
|
* @skb: buffer where extension header data starts in packet
|
||||||
|
* @nh: ipv6 header
|
||||||
|
* @ext_hdrs: flags are stored here
|
||||||
|
*
|
||||||
|
* OFPIEH12_UNREP is set if more than one of a given IPv6 extension header
|
||||||
|
* is unexpectedly encountered. (Two destination options headers may be
|
||||||
|
* expected and would not cause this bit to be set.)
|
||||||
|
*
|
||||||
|
* OFPIEH12_UNSEQ is set if IPv6 extension headers were not in the order
|
||||||
|
* preferred (but not required) by RFC 2460:
|
||||||
|
*
|
||||||
|
* When more than one extension header is used in the same packet, it is
|
||||||
|
* recommended that those headers appear in the following order:
|
||||||
|
* IPv6 header
|
||||||
|
* Hop-by-Hop Options header
|
||||||
|
* Destination Options header
|
||||||
|
* Routing header
|
||||||
|
* Fragment header
|
||||||
|
* Authentication header
|
||||||
|
* Encapsulating Security Payload header
|
||||||
|
* Destination Options header
|
||||||
|
* upper-layer header
|
||||||
|
*/
|
||||||
|
static void get_ipv6_ext_hdrs(struct sk_buff *skb, struct ipv6hdr *nh,
|
||||||
|
u16 *ext_hdrs)
|
||||||
|
{
|
||||||
|
u8 next_type = nh->nexthdr;
|
||||||
|
unsigned int start = skb_network_offset(skb) + sizeof(struct ipv6hdr);
|
||||||
|
int dest_options_header_count = 0;
|
||||||
|
|
||||||
|
*ext_hdrs = 0;
|
||||||
|
|
||||||
|
while (ipv6_ext_hdr(next_type)) {
|
||||||
|
struct ipv6_opt_hdr _hdr, *hp;
|
||||||
|
|
||||||
|
switch (next_type) {
|
||||||
|
case IPPROTO_NONE:
|
||||||
|
*ext_hdrs |= OFPIEH12_NONEXT;
|
||||||
|
/* stop parsing */
|
||||||
|
return;
|
||||||
|
|
||||||
|
case IPPROTO_ESP:
|
||||||
|
if (*ext_hdrs & OFPIEH12_ESP)
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
if ((*ext_hdrs & ~(OFPIEH12_HOP | OFPIEH12_DEST |
|
||||||
|
OFPIEH12_ROUTER | IPPROTO_FRAGMENT |
|
||||||
|
OFPIEH12_AUTH | OFPIEH12_UNREP)) ||
|
||||||
|
dest_options_header_count >= 2) {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
}
|
||||||
|
*ext_hdrs |= OFPIEH12_ESP;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IPPROTO_AH:
|
||||||
|
if (*ext_hdrs & OFPIEH12_AUTH)
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
if ((*ext_hdrs &
|
||||||
|
~(OFPIEH12_HOP | OFPIEH12_DEST | OFPIEH12_ROUTER |
|
||||||
|
IPPROTO_FRAGMENT | OFPIEH12_UNREP)) ||
|
||||||
|
dest_options_header_count >= 2) {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
}
|
||||||
|
*ext_hdrs |= OFPIEH12_AUTH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IPPROTO_DSTOPTS:
|
||||||
|
if (dest_options_header_count == 0) {
|
||||||
|
if (*ext_hdrs &
|
||||||
|
~(OFPIEH12_HOP | OFPIEH12_UNREP))
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
*ext_hdrs |= OFPIEH12_DEST;
|
||||||
|
} else if (dest_options_header_count == 1) {
|
||||||
|
if (*ext_hdrs &
|
||||||
|
~(OFPIEH12_HOP | OFPIEH12_DEST |
|
||||||
|
OFPIEH12_ROUTER | OFPIEH12_FRAG |
|
||||||
|
OFPIEH12_AUTH | OFPIEH12_ESP |
|
||||||
|
OFPIEH12_UNREP)) {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
}
|
||||||
|
dest_options_header_count++;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IPPROTO_FRAGMENT:
|
||||||
|
if (*ext_hdrs & OFPIEH12_FRAG)
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
if ((*ext_hdrs & ~(OFPIEH12_HOP |
|
||||||
|
OFPIEH12_DEST |
|
||||||
|
OFPIEH12_ROUTER |
|
||||||
|
OFPIEH12_UNREP)) ||
|
||||||
|
dest_options_header_count >= 2) {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
}
|
||||||
|
*ext_hdrs |= OFPIEH12_FRAG;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IPPROTO_ROUTING:
|
||||||
|
if (*ext_hdrs & OFPIEH12_ROUTER)
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
if ((*ext_hdrs & ~(OFPIEH12_HOP |
|
||||||
|
OFPIEH12_DEST |
|
||||||
|
OFPIEH12_UNREP)) ||
|
||||||
|
dest_options_header_count >= 2) {
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
}
|
||||||
|
*ext_hdrs |= OFPIEH12_ROUTER;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IPPROTO_HOPOPTS:
|
||||||
|
if (*ext_hdrs & OFPIEH12_HOP)
|
||||||
|
*ext_hdrs |= OFPIEH12_UNREP;
|
||||||
|
/* OFPIEH12_HOP is set to 1 if a hop-by-hop IPv6
|
||||||
|
* extension header is present as the first
|
||||||
|
* extension header in the packet.
|
||||||
|
*/
|
||||||
|
if (*ext_hdrs == 0)
|
||||||
|
*ext_hdrs |= OFPIEH12_HOP;
|
||||||
|
else
|
||||||
|
*ext_hdrs |= OFPIEH12_UNSEQ;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hp = skb_header_pointer(skb, start, sizeof(_hdr), &_hdr);
|
||||||
|
if (!hp)
|
||||||
|
break;
|
||||||
|
next_type = hp->nexthdr;
|
||||||
|
start += ipv6_optlen(hp);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
|
static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
|
||||||
{
|
{
|
||||||
unsigned short frag_off;
|
unsigned short frag_off;
|
||||||
|
@ -256,6 +394,8 @@ static int parse_ipv6hdr(struct sk_buff *skb, struct sw_flow_key *key)
|
||||||
|
|
||||||
nh = ipv6_hdr(skb);
|
nh = ipv6_hdr(skb);
|
||||||
|
|
||||||
|
get_ipv6_ext_hdrs(skb, nh, &key->ipv6.exthdrs);
|
||||||
|
|
||||||
key->ip.proto = NEXTHDR_NONE;
|
key->ip.proto = NEXTHDR_NONE;
|
||||||
key->ip.tos = ipv6_get_dsfield(nh);
|
key->ip.tos = ipv6_get_dsfield(nh);
|
||||||
key->ip.ttl = nh->hop_limit;
|
key->ip.ttl = nh->hop_limit;
|
||||||
|
|
|
@ -32,6 +32,19 @@ enum sw_flow_mac_proto {
|
||||||
#define SW_FLOW_KEY_INVALID 0x80
|
#define SW_FLOW_KEY_INVALID 0x80
|
||||||
#define MPLS_LABEL_DEPTH 3
|
#define MPLS_LABEL_DEPTH 3
|
||||||
|
|
||||||
|
/* Bit definitions for IPv6 Extension Header pseudo-field. */
|
||||||
|
enum ofp12_ipv6exthdr_flags {
|
||||||
|
OFPIEH12_NONEXT = 1 << 0, /* "No next header" encountered. */
|
||||||
|
OFPIEH12_ESP = 1 << 1, /* Encrypted Sec Payload header present. */
|
||||||
|
OFPIEH12_AUTH = 1 << 2, /* Authentication header present. */
|
||||||
|
OFPIEH12_DEST = 1 << 3, /* 1 or 2 dest headers present. */
|
||||||
|
OFPIEH12_FRAG = 1 << 4, /* Fragment header present. */
|
||||||
|
OFPIEH12_ROUTER = 1 << 5, /* Router header present. */
|
||||||
|
OFPIEH12_HOP = 1 << 6, /* Hop-by-hop header present. */
|
||||||
|
OFPIEH12_UNREP = 1 << 7, /* Unexpected repeats encountered. */
|
||||||
|
OFPIEH12_UNSEQ = 1 << 8 /* Unexpected sequencing encountered. */
|
||||||
|
};
|
||||||
|
|
||||||
/* Store options at the end of the array if they are less than the
|
/* Store options at the end of the array if they are less than the
|
||||||
* maximum size. This allows us to get the benefits of variable length
|
* maximum size. This allows us to get the benefits of variable length
|
||||||
* matching for small options.
|
* matching for small options.
|
||||||
|
@ -121,6 +134,7 @@ struct sw_flow_key {
|
||||||
struct in6_addr dst; /* IPv6 destination address. */
|
struct in6_addr dst; /* IPv6 destination address. */
|
||||||
} addr;
|
} addr;
|
||||||
__be32 label; /* IPv6 flow label. */
|
__be32 label; /* IPv6 flow label. */
|
||||||
|
u16 exthdrs; /* IPv6 extension header flags */
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
struct in6_addr src;
|
struct in6_addr src;
|
||||||
|
|
|
@ -346,7 +346,7 @@ size_t ovs_key_attr_size(void)
|
||||||
/* Whenever adding new OVS_KEY_ FIELDS, we should consider
|
/* Whenever adding new OVS_KEY_ FIELDS, we should consider
|
||||||
* updating this function.
|
* updating this function.
|
||||||
*/
|
*/
|
||||||
BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 29);
|
BUILD_BUG_ON(OVS_KEY_ATTR_TUNNEL_INFO != 30);
|
||||||
|
|
||||||
return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
|
return nla_total_size(4) /* OVS_KEY_ATTR_PRIORITY */
|
||||||
+ nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
|
+ nla_total_size(0) /* OVS_KEY_ATTR_TUNNEL */
|
||||||
|
@ -369,7 +369,8 @@ size_t ovs_key_attr_size(void)
|
||||||
+ nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
|
+ nla_total_size(2) /* OVS_KEY_ATTR_ETHERTYPE */
|
||||||
+ nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
|
+ nla_total_size(40) /* OVS_KEY_ATTR_IPV6 */
|
||||||
+ nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
|
+ nla_total_size(2) /* OVS_KEY_ATTR_ICMPV6 */
|
||||||
+ nla_total_size(28); /* OVS_KEY_ATTR_ND */
|
+ nla_total_size(28) /* OVS_KEY_ATTR_ND */
|
||||||
|
+ nla_total_size(2); /* OVS_KEY_ATTR_IPV6_EXTHDRS */
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = {
|
static const struct ovs_len_tbl ovs_vxlan_ext_key_lens[OVS_VXLAN_EXT_MAX + 1] = {
|
||||||
|
@ -437,6 +438,8 @@ static const struct ovs_len_tbl ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
|
||||||
.len = sizeof(struct ovs_key_ct_tuple_ipv6) },
|
.len = sizeof(struct ovs_key_ct_tuple_ipv6) },
|
||||||
[OVS_KEY_ATTR_NSH] = { .len = OVS_ATTR_NESTED,
|
[OVS_KEY_ATTR_NSH] = { .len = OVS_ATTR_NESTED,
|
||||||
.next = ovs_nsh_key_attr_lens, },
|
.next = ovs_nsh_key_attr_lens, },
|
||||||
|
[OVS_KEY_ATTR_IPV6_EXTHDRS] = {
|
||||||
|
.len = sizeof(struct ovs_key_ipv6_exthdrs) },
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool check_attr_len(unsigned int attr_len, unsigned int expected_len)
|
static bool check_attr_len(unsigned int attr_len, unsigned int expected_len)
|
||||||
|
@ -1597,6 +1600,17 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match,
|
||||||
attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
|
attrs &= ~(1 << OVS_KEY_ATTR_IPV6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attrs & (1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS)) {
|
||||||
|
const struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key;
|
||||||
|
|
||||||
|
ipv6_exthdrs_key = nla_data(a[OVS_KEY_ATTR_IPV6_EXTHDRS]);
|
||||||
|
|
||||||
|
SW_FLOW_KEY_PUT(match, ipv6.exthdrs,
|
||||||
|
ipv6_exthdrs_key->hdrs, is_mask);
|
||||||
|
|
||||||
|
attrs &= ~(1ULL << OVS_KEY_ATTR_IPV6_EXTHDRS);
|
||||||
|
}
|
||||||
|
|
||||||
if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
|
if (attrs & (1 << OVS_KEY_ATTR_ARP)) {
|
||||||
const struct ovs_key_arp *arp_key;
|
const struct ovs_key_arp *arp_key;
|
||||||
|
|
||||||
|
@ -2099,6 +2113,7 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
||||||
ipv4_key->ipv4_frag = output->ip.frag;
|
ipv4_key->ipv4_frag = output->ip.frag;
|
||||||
} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
|
} else if (swkey->eth.type == htons(ETH_P_IPV6)) {
|
||||||
struct ovs_key_ipv6 *ipv6_key;
|
struct ovs_key_ipv6 *ipv6_key;
|
||||||
|
struct ovs_key_ipv6_exthdrs *ipv6_exthdrs_key;
|
||||||
|
|
||||||
nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));
|
nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6, sizeof(*ipv6_key));
|
||||||
if (!nla)
|
if (!nla)
|
||||||
|
@ -2113,6 +2128,13 @@ static int __ovs_nla_put_key(const struct sw_flow_key *swkey,
|
||||||
ipv6_key->ipv6_tclass = output->ip.tos;
|
ipv6_key->ipv6_tclass = output->ip.tos;
|
||||||
ipv6_key->ipv6_hlimit = output->ip.ttl;
|
ipv6_key->ipv6_hlimit = output->ip.ttl;
|
||||||
ipv6_key->ipv6_frag = output->ip.frag;
|
ipv6_key->ipv6_frag = output->ip.frag;
|
||||||
|
|
||||||
|
nla = nla_reserve(skb, OVS_KEY_ATTR_IPV6_EXTHDRS,
|
||||||
|
sizeof(*ipv6_exthdrs_key));
|
||||||
|
if (!nla)
|
||||||
|
goto nla_put_failure;
|
||||||
|
ipv6_exthdrs_key = nla_data(nla);
|
||||||
|
ipv6_exthdrs_key->hdrs = output->ipv6.exthdrs;
|
||||||
} else if (swkey->eth.type == htons(ETH_P_NSH)) {
|
} else if (swkey->eth.type == htons(ETH_P_NSH)) {
|
||||||
if (nsh_key_to_nlattr(&output->nsh, is_mask, skb))
|
if (nsh_key_to_nlattr(&output->nsh, is_mask, skb))
|
||||||
goto nla_put_failure;
|
goto nla_put_failure;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue