2
0
mirror of https://github.com/openvswitch/ovs synced 2025-10-19 14:37:21 +00:00
Files
openvswitch/lib/ofp-parse.c
Numan Siddique 5b34f8fc3b Add a new OVS action check_pkt_larger
This patch adds a new action 'check_pkt_larger' which checks if the
packet is larger than the given size and stores the result in the
destination register.

Usage: check_pkt_larger(len)->REGISTER
Eg. match=...,actions=check_pkt_larger(1442)->NXM_NX_REG0[0],next;

This patch makes use of the new datapath action - 'check_pkt_len'
which was recently added in the commit [1].
At the start of ovs-vswitchd, datapath is probed for this action.
If the datapath action is present, then 'check_pkt_larger'
makes use of this datapath action.

Datapath action 'check_pkt_len' takes these nlattrs
      * OVS_CHECK_PKT_LEN_ATTR_PKT_LEN - 'pkt_len' to check for
      * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_GREATER (optional) - Nested actions
        to apply if the packet length is greater than the specified 'pkt_len'
      * OVS_CHECK_PKT_LEN_ATTR_ACTIONS_IF_LESS_EQUAL (optional) - Nested
        actions to apply if the packet length is lesser or equal to the
        specified 'pkt_len'.

Let's say we have these flows added to an OVS bridge br-int

table=0, priority=100 in_port=1,ip,actions=check_pkt_larger:100->NXM_NX_REG0[0],resubmit(,1)
table=1, priority=200,in_port=1,ip,reg0=0x1/0x1 actions=output:3
table=1, priority=100,in_port=1,ip,actions=output:4

Then the action 'check_pkt_larger' will be translated as
  - check_pkt_len(size=100,gt(3),le(4))

datapath will check the packet length and if the packet length is greater than 100,
it will output to port 3, else it will output to port 4.

In case, datapath doesn't support 'check_pkt_len' action, the OVS action
'check_pkt_larger' sets SLOW_ACTION so that datapath flow is not added.

This OVS action is intended to be used by OVN to check the packet length
and generate an ICMP packet with type 3, code 4 and next hop mtu
in the logical router pipeline if the MTU of the physical interface
is lesser than the packet length. More information can be found here [2]

[1] - 4d5ec89fc8
[2] - https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html

Reported-at:
https://mail.openvswitch.org/pipermail/ovs-discuss/2018-July/047039.html
Suggested-by: Ben Pfaff <blp@ovn.org>
Signed-off-by: Numan Siddique <nusiddiq@redhat.com>
CC: Ben Pfaff <blp@ovn.org>
CC: Gregory Rose <gvrose8192@gmail.com>
Acked-by: Mark Michelson <mmichels@redhat.com>
Signed-off-by: Ben Pfaff <blp@ovn.org>
2019-04-22 12:56:50 -07:00

355 lines
11 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <config.h>
#include "openvswitch/ofp-parse.h"
#include <errno.h>
#include "byte-order.h"
#include "openvswitch/match.h"
#include "openvswitch/meta-flow.h"
#include "openvswitch/ofp-actions.h"
#include "openvswitch/ofp-flow.h"
#include "openvswitch/ofp-match.h"
#include "openvswitch/ofp-table.h"
#include "packets.h"
#include "socket-util.h"
#include "util.h"
/* Parses 'str' as an 8-bit unsigned integer into '*valuep'.
*
* 'name' describes the value parsed in an error message, if any.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_u8(const char *str, const char *name, uint8_t *valuep)
{
int value;
if (!str_to_int(str, 0, &value) || value < 0 || value > 255) {
return xasprintf("invalid %s \"%s\"", name, str);
}
*valuep = value;
return NULL;
}
/* Parses 'str' as a 16-bit unsigned integer into '*valuep'.
*
* 'name' describes the value parsed in an error message, if any.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_u16(const char *str, const char *name, uint16_t *valuep)
{
int value;
if (!str_to_int(str, 0, &value) || value < 0 || value > 65535) {
return xasprintf("invalid %s \"%s\"", name, str);
}
*valuep = value;
return NULL;
}
/* Parses 'str' as a 32-bit unsigned integer into '*valuep'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_u32(const char *str, uint32_t *valuep)
{
char *tail;
uint32_t value;
if (!str[0]) {
return xstrdup("missing required numeric argument");
}
errno = 0;
value = strtoul(str, &tail, 0);
if (errno == EINVAL || errno == ERANGE || *tail) {
return xasprintf("invalid numeric format %s", str);
}
*valuep = value;
return NULL;
}
/* Parses 'str' as an 64-bit unsigned integer into '*valuep'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_u64(const char *str, uint64_t *valuep)
{
char *tail;
uint64_t value;
if (!str[0]) {
return xstrdup("missing required numeric argument");
}
errno = 0;
value = strtoull(str, &tail, 0);
if (errno == EINVAL || errno == ERANGE || *tail) {
return xasprintf("invalid numeric format %s", str);
}
*valuep = value;
return NULL;
}
/* Parses 'str' as an 64-bit unsigned integer in network byte order into
* '*valuep'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_be64(const char *str, ovs_be64 *valuep)
{
uint64_t value = 0;
char *error;
error = str_to_u64(str, &value);
if (!error) {
*valuep = htonll(value);
}
return error;
}
/* Parses 'str' as an Ethernet address into 'mac'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_mac(const char *str, struct eth_addr *mac)
{
if (!ovs_scan(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*mac))) {
return xasprintf("invalid mac address %s", str);
}
return NULL;
}
/* Parses 'str' as an IP address into '*ip'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_ip(const char *str, ovs_be32 *ip)
{
struct in_addr in_addr;
if (lookup_ip(str, &in_addr)) {
return xasprintf("%s: could not convert to IP address", str);
}
*ip = in_addr.s_addr;
return NULL;
}
/* Parses 'str' as a conntrack helper into 'alg'.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
str_to_connhelper(const char *str, uint16_t *alg)
{
if (!strcmp(str, "ftp")) {
*alg = IPPORT_FTP;
return NULL;
}
if (!strcmp(str, "tftp")) {
*alg = IPPORT_TFTP;
return NULL;
}
return xasprintf("invalid conntrack helper \"%s\"", str);
}
bool
ofp_parse_protocol(const char *name, const struct ofp_protocol **p_out)
{
static const struct ofp_protocol protocols[] = {
{ "ip", ETH_TYPE_IP, 0 },
{ "ipv4", ETH_TYPE_IP, 0 },
{ "ip4", ETH_TYPE_IP, 0 },
{ "arp", ETH_TYPE_ARP, 0 },
{ "icmp", ETH_TYPE_IP, IPPROTO_ICMP },
{ "tcp", ETH_TYPE_IP, IPPROTO_TCP },
{ "udp", ETH_TYPE_IP, IPPROTO_UDP },
{ "sctp", ETH_TYPE_IP, IPPROTO_SCTP },
{ "ipv6", ETH_TYPE_IPV6, 0 },
{ "ip6", ETH_TYPE_IPV6, 0 },
{ "icmp6", ETH_TYPE_IPV6, IPPROTO_ICMPV6 },
{ "tcp6", ETH_TYPE_IPV6, IPPROTO_TCP },
{ "udp6", ETH_TYPE_IPV6, IPPROTO_UDP },
{ "sctp6", ETH_TYPE_IPV6, IPPROTO_SCTP },
{ "rarp", ETH_TYPE_RARP, 0},
{ "mpls", ETH_TYPE_MPLS, 0 },
{ "mplsm", ETH_TYPE_MPLS_MCAST, 0 },
};
const struct ofp_protocol *p;
for (p = protocols; p < &protocols[ARRAY_SIZE(protocols)]; p++) {
if (!strcmp(p->name, name)) {
*p_out = p;
return true;
}
}
*p_out = NULL;
return false;
}
/* Parses 's' as the (possibly masked) value of field 'mf', and updates
* 'match' appropriately. Restricts the set of usable protocols to ones
* supporting the parsed field.
*
* Returns NULL if successful, otherwise a malloc()'d string describing the
* error. The caller is responsible for freeing the returned string. */
char * OVS_WARN_UNUSED_RESULT
ofp_parse_field(const struct mf_field *mf, const char *s,
const struct ofputil_port_map *port_map, struct match *match,
enum ofputil_protocol *usable_protocols)
{
union mf_value value, mask;
char *error;
if (!*s) {
/* If there's no string, we're just trying to match on the
* existence of the field, so use a no-op value. */
s = "0/0";
}
error = mf_parse(mf, s, port_map, &value, &mask);
if (!error) {
*usable_protocols &= mf_set(mf, &value, &mask, match, &error);
match_add_ethernet_prereq(match, mf);
}
return error;
}
char *
ofp_extract_actions(char *s)
{
s = strstr(s, "action");
if (s) {
*s = '\0';
s = strchr(s + 1, '=');
return s ? s + 1 : NULL;
} else {
return NULL;
}
}
static size_t
parse_value(const char *s, const char *delimiters)
{
size_t n = 0;
/* Iterate until we reach a delimiter.
*
* strchr(s, '\0') returns s+strlen(s), so this test handles the null
* terminator at the end of 's'. */
while (!strchr(delimiters, s[n])) {
if (s[n] == '(') {
int level = 0;
do {
switch (s[n]) {
case '\0':
return n;
case '(':
level++;
break;
case ')':
level--;
break;
}
n++;
} while (level > 0);
} else {
n++;
}
}
return n;
}
/* Parses a key or a key-value pair from '*stringp'.
*
* On success: Stores the key into '*keyp'. Stores the value, if present, into
* '*valuep', otherwise an empty string. Advances '*stringp' past the end of
* the key-value pair, preparing it for another call. '*keyp' and '*valuep'
* are substrings of '*stringp' created by replacing some of its bytes by null
* terminators. Returns true.
*
* If '*stringp' is just white space or commas, sets '*keyp' and '*valuep' to
* NULL and returns false. */
bool
ofputil_parse_key_value(char **stringp, char **keyp, char **valuep)
{
/* Skip white space and delimiters. If that brings us to the end of the
* input string, we are done and there are no more key-value pairs. */
*stringp += strspn(*stringp, ", \t\r\n");
if (**stringp == '\0') {
*keyp = *valuep = NULL;
return false;
}
/* Extract the key and the delimiter that ends the key-value pair or begins
* the value. Advance the input position past the key and delimiter. */
char *key = *stringp;
size_t key_len = strcspn(key, ":=(, \t\r\n");
char key_delim = key[key_len];
key[key_len] = '\0';
*stringp += key_len + (key_delim != '\0');
/* Figure out what delimiter ends the value:
*
* - If key_delim is ":" or "=", the value extends until white space
* or a comma.
*
* - If key_delim is "(", the value extends until ")".
*
* If there is no value, we are done. */
const char *value_delims;
if (key_delim == ':' || key_delim == '=') {
value_delims = ", \t\r\n";
} else if (key_delim == '(') {
value_delims = ")";
} else {
*keyp = key;
*valuep = key + key_len; /* Empty string. */
return true;
}
/* Extract the value. Advance the input position past the value and
* delimiter. */
char *value = *stringp;
size_t value_len = parse_value(value, value_delims);
char value_delim = value[value_len];
/* Handle the special case if the value is of the form "(x)->y".
* After parsing, 'valuep' will be pointing to - "x)->y".
* */
if (key_delim == '(' && value[value_len] == ')' &&
value[value_len + 1] == '-' && value[value_len + 2] == '>') {
value_delims = ", \t\r\n";
value_len += parse_value(&value[value_len], value_delims);
value_delim = value[value_len];
}
value[value_len] = '\0';
*stringp += value_len + (value_delim != '\0');
*keyp = key;
*valuep = value;
return true;
}