mirror of
https://github.com/openvswitch/ovs
synced 2025-10-19 14:37:21 +00:00
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>
355 lines
11 KiB
C
355 lines
11 KiB
C
/*
|
||
* 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;
|
||
}
|