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:
Toms Atteka
2022-02-23 16:54:09 -08:00
committed by David S. Miller
parent a46e3d5eb7
commit 28a3f06017
4 changed files with 184 additions and 2 deletions

View File

@@ -241,6 +241,144 @@ static bool icmphdr_ok(struct sk_buff *skb)
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)
{
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);
get_ipv6_ext_hdrs(skb, nh, &key->ipv6.exthdrs);
key->ip.proto = NEXTHDR_NONE;
key->ip.tos = ipv6_get_dsfield(nh);
key->ip.ttl = nh->hop_limit;