mirror of
https://github.com/openvswitch/ovs
synced 2025-10-21 14:49:41 +00:00
Since the Nicira Extended Match was specified nicira-ext.h has claimed that arbitrary masks are allowed, but in fact only certain masks were actually implemented. This commit implements general masking for the 802.1Q VLAN TCI field.
659 lines
20 KiB
C
659 lines
20 KiB
C
/*
|
|
* Copyright (c) 2010 Nicira Networks.
|
|
*
|
|
* 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 "ofp-parse.h"
|
|
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "byte-order.h"
|
|
#include "dynamic-string.h"
|
|
#include "netdev.h"
|
|
#include "ofp-util.h"
|
|
#include "ofpbuf.h"
|
|
#include "openflow/openflow.h"
|
|
#include "packets.h"
|
|
#include "socket-util.h"
|
|
#include "vconn.h"
|
|
#include "vlog.h"
|
|
|
|
VLOG_DEFINE_THIS_MODULE(ofp_parse);
|
|
|
|
static uint32_t
|
|
str_to_u32(const char *str)
|
|
{
|
|
char *tail;
|
|
uint32_t value;
|
|
|
|
if (!str) {
|
|
ovs_fatal(0, "missing required numeric argument");
|
|
}
|
|
|
|
errno = 0;
|
|
value = strtoul(str, &tail, 0);
|
|
if (errno == EINVAL || errno == ERANGE || *tail) {
|
|
ovs_fatal(0, "invalid numeric format %s", str);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static uint64_t
|
|
str_to_u64(const char *str)
|
|
{
|
|
char *tail;
|
|
uint64_t value;
|
|
|
|
errno = 0;
|
|
value = strtoull(str, &tail, 0);
|
|
if (errno == EINVAL || errno == ERANGE || *tail) {
|
|
ovs_fatal(0, "invalid numeric format %s", str);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static void
|
|
str_to_mac(const char *str, uint8_t mac[6])
|
|
{
|
|
if (sscanf(str, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))
|
|
!= ETH_ADDR_SCAN_COUNT) {
|
|
ovs_fatal(0, "invalid mac address %s", str);
|
|
}
|
|
}
|
|
|
|
static void
|
|
str_to_ip(const char *str_, ovs_be32 *ip, ovs_be32 *maskp)
|
|
{
|
|
char *str = xstrdup(str_);
|
|
char *save_ptr = NULL;
|
|
const char *name, *netmask;
|
|
struct in_addr in_addr;
|
|
ovs_be32 mask;
|
|
int retval;
|
|
|
|
name = strtok_r(str, "/", &save_ptr);
|
|
retval = name ? lookup_ip(name, &in_addr) : EINVAL;
|
|
if (retval) {
|
|
ovs_fatal(0, "%s: could not convert to IP address", str);
|
|
}
|
|
*ip = in_addr.s_addr;
|
|
|
|
netmask = strtok_r(NULL, "/", &save_ptr);
|
|
if (netmask) {
|
|
uint8_t o[4];
|
|
if (sscanf(netmask, "%"SCNu8".%"SCNu8".%"SCNu8".%"SCNu8,
|
|
&o[0], &o[1], &o[2], &o[3]) == 4) {
|
|
mask = htonl((o[0] << 24) | (o[1] << 16) | (o[2] << 8) | o[3]);
|
|
} else {
|
|
int prefix = atoi(netmask);
|
|
if (prefix <= 0 || prefix > 32) {
|
|
ovs_fatal(0, "%s: network prefix bits not between 1 and 32",
|
|
str);
|
|
} else if (prefix == 32) {
|
|
mask = htonl(UINT32_MAX);
|
|
} else {
|
|
mask = htonl(((1u << prefix) - 1) << (32 - prefix));
|
|
}
|
|
}
|
|
} else {
|
|
mask = htonl(UINT32_MAX);
|
|
}
|
|
*ip &= mask;
|
|
|
|
if (maskp) {
|
|
*maskp = mask;
|
|
} else {
|
|
if (mask != htonl(UINT32_MAX)) {
|
|
ovs_fatal(0, "%s: netmask not allowed here", str_);
|
|
}
|
|
}
|
|
|
|
free(str);
|
|
}
|
|
|
|
static void *
|
|
put_action(struct ofpbuf *b, size_t size, uint16_t type)
|
|
{
|
|
struct ofp_action_header *ah = ofpbuf_put_zeros(b, size);
|
|
ah->type = htons(type);
|
|
ah->len = htons(size);
|
|
return ah;
|
|
}
|
|
|
|
static struct ofp_action_output *
|
|
put_output_action(struct ofpbuf *b, uint16_t port)
|
|
{
|
|
struct ofp_action_output *oao = put_action(b, sizeof *oao, OFPAT_OUTPUT);
|
|
oao->port = htons(port);
|
|
return oao;
|
|
}
|
|
|
|
static void
|
|
put_enqueue_action(struct ofpbuf *b, uint16_t port, uint32_t queue)
|
|
{
|
|
struct ofp_action_enqueue *oae = put_action(b, sizeof *oae, OFPAT_ENQUEUE);
|
|
oae->port = htons(port);
|
|
oae->queue_id = htonl(queue);
|
|
}
|
|
|
|
static void
|
|
put_dl_addr_action(struct ofpbuf *b, uint16_t type, const char *addr)
|
|
{
|
|
struct ofp_action_dl_addr *oada = put_action(b, sizeof *oada, type);
|
|
str_to_mac(addr, oada->dl_addr);
|
|
}
|
|
|
|
|
|
static bool
|
|
parse_port_name(const char *name, uint16_t *port)
|
|
{
|
|
struct pair {
|
|
const char *name;
|
|
uint16_t value;
|
|
};
|
|
static const struct pair pairs[] = {
|
|
#define DEF_PAIR(NAME) {#NAME, OFPP_##NAME}
|
|
DEF_PAIR(IN_PORT),
|
|
DEF_PAIR(TABLE),
|
|
DEF_PAIR(NORMAL),
|
|
DEF_PAIR(FLOOD),
|
|
DEF_PAIR(ALL),
|
|
DEF_PAIR(CONTROLLER),
|
|
DEF_PAIR(LOCAL),
|
|
DEF_PAIR(NONE),
|
|
#undef DEF_PAIR
|
|
};
|
|
static const int n_pairs = ARRAY_SIZE(pairs);
|
|
size_t i;
|
|
|
|
for (i = 0; i < n_pairs; i++) {
|
|
if (!strcasecmp(name, pairs[i].name)) {
|
|
*port = pairs[i].value;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
str_to_action(char *str, struct ofpbuf *b)
|
|
{
|
|
char *act, *arg;
|
|
char *saveptr = NULL;
|
|
bool drop = false;
|
|
int n_actions;
|
|
|
|
for (act = strtok_r(str, ", \t\r\n", &saveptr), n_actions = 0; act;
|
|
act = strtok_r(NULL, ", \t\r\n", &saveptr), n_actions++)
|
|
{
|
|
uint16_t port;
|
|
|
|
if (drop) {
|
|
ovs_fatal(0, "Drop actions must not be followed by other actions");
|
|
}
|
|
|
|
/* Arguments are separated by colons */
|
|
arg = strchr(act, ':');
|
|
if (arg) {
|
|
*arg = '\0';
|
|
arg++;
|
|
}
|
|
|
|
if (!strcasecmp(act, "mod_vlan_vid")) {
|
|
struct ofp_action_vlan_vid *va;
|
|
va = put_action(b, sizeof *va, OFPAT_SET_VLAN_VID);
|
|
va->vlan_vid = htons(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "mod_vlan_pcp")) {
|
|
struct ofp_action_vlan_pcp *va;
|
|
va = put_action(b, sizeof *va, OFPAT_SET_VLAN_PCP);
|
|
va->vlan_pcp = str_to_u32(arg);
|
|
} else if (!strcasecmp(act, "strip_vlan")) {
|
|
struct ofp_action_header *ah;
|
|
ah = put_action(b, sizeof *ah, OFPAT_STRIP_VLAN);
|
|
ah->type = htons(OFPAT_STRIP_VLAN);
|
|
} else if (!strcasecmp(act, "mod_dl_src")) {
|
|
put_dl_addr_action(b, OFPAT_SET_DL_SRC, arg);
|
|
} else if (!strcasecmp(act, "mod_dl_dst")) {
|
|
put_dl_addr_action(b, OFPAT_SET_DL_DST, arg);
|
|
} else if (!strcasecmp(act, "mod_nw_src")) {
|
|
struct ofp_action_nw_addr *na;
|
|
na = put_action(b, sizeof *na, OFPAT_SET_NW_SRC);
|
|
str_to_ip(arg, &na->nw_addr, NULL);
|
|
} else if (!strcasecmp(act, "mod_nw_dst")) {
|
|
struct ofp_action_nw_addr *na;
|
|
na = put_action(b, sizeof *na, OFPAT_SET_NW_DST);
|
|
str_to_ip(arg, &na->nw_addr, NULL);
|
|
} else if (!strcasecmp(act, "mod_tp_src")) {
|
|
struct ofp_action_tp_port *ta;
|
|
ta = put_action(b, sizeof *ta, OFPAT_SET_TP_SRC);
|
|
ta->tp_port = htons(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "mod_tp_dst")) {
|
|
struct ofp_action_tp_port *ta;
|
|
ta = put_action(b, sizeof *ta, OFPAT_SET_TP_DST);
|
|
ta->tp_port = htons(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "mod_nw_tos")) {
|
|
struct ofp_action_nw_tos *nt;
|
|
nt = put_action(b, sizeof *nt, OFPAT_SET_NW_TOS);
|
|
nt->nw_tos = str_to_u32(arg);
|
|
} else if (!strcasecmp(act, "resubmit")) {
|
|
struct nx_action_resubmit *nar;
|
|
nar = put_action(b, sizeof *nar, OFPAT_VENDOR);
|
|
nar->vendor = htonl(NX_VENDOR_ID);
|
|
nar->subtype = htons(NXAST_RESUBMIT);
|
|
nar->in_port = htons(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "set_tunnel")) {
|
|
struct nx_action_set_tunnel *nast;
|
|
nast = put_action(b, sizeof *nast, OFPAT_VENDOR);
|
|
nast->vendor = htonl(NX_VENDOR_ID);
|
|
nast->subtype = htons(NXAST_SET_TUNNEL);
|
|
nast->tun_id = htonl(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "drop_spoofed_arp")) {
|
|
struct nx_action_header *nah;
|
|
nah = put_action(b, sizeof *nah, OFPAT_VENDOR);
|
|
nah->vendor = htonl(NX_VENDOR_ID);
|
|
nah->subtype = htons(NXAST_DROP_SPOOFED_ARP);
|
|
} else if (!strcasecmp(act, "set_queue")) {
|
|
struct nx_action_set_queue *nasq;
|
|
nasq = put_action(b, sizeof *nasq, OFPAT_VENDOR);
|
|
nasq->vendor = htonl(NX_VENDOR_ID);
|
|
nasq->subtype = htons(NXAST_SET_QUEUE);
|
|
nasq->queue_id = htonl(str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "pop_queue")) {
|
|
struct nx_action_header *nah;
|
|
nah = put_action(b, sizeof *nah, OFPAT_VENDOR);
|
|
nah->vendor = htonl(NX_VENDOR_ID);
|
|
nah->subtype = htons(NXAST_POP_QUEUE);
|
|
} else if (!strcasecmp(act, "note")) {
|
|
size_t start_ofs = b->size;
|
|
struct nx_action_note *nan;
|
|
int remainder;
|
|
size_t len;
|
|
|
|
nan = put_action(b, sizeof *nan, OFPAT_VENDOR);
|
|
nan->vendor = htonl(NX_VENDOR_ID);
|
|
nan->subtype = htons(NXAST_NOTE);
|
|
|
|
b->size -= sizeof nan->note;
|
|
while (arg && *arg != '\0') {
|
|
uint8_t byte;
|
|
bool ok;
|
|
|
|
if (*arg == '.') {
|
|
arg++;
|
|
}
|
|
if (*arg == '\0') {
|
|
break;
|
|
}
|
|
|
|
byte = hexits_value(arg, 2, &ok);
|
|
if (!ok) {
|
|
ovs_fatal(0, "bad hex digit in `note' argument");
|
|
}
|
|
ofpbuf_put(b, &byte, 1);
|
|
|
|
arg += 2;
|
|
}
|
|
|
|
len = b->size - start_ofs;
|
|
remainder = len % OFP_ACTION_ALIGN;
|
|
if (remainder) {
|
|
ofpbuf_put_zeros(b, OFP_ACTION_ALIGN - remainder);
|
|
}
|
|
nan->len = htons(b->size - start_ofs);
|
|
} else if (!strcasecmp(act, "output")) {
|
|
put_output_action(b, str_to_u32(arg));
|
|
} else if (!strcasecmp(act, "enqueue")) {
|
|
char *sp = NULL;
|
|
char *port_s = strtok_r(arg, ":q", &sp);
|
|
char *queue = strtok_r(NULL, "", &sp);
|
|
if (port_s == NULL || queue == NULL) {
|
|
ovs_fatal(0, "\"enqueue\" syntax is \"enqueue:PORT:QUEUE\"");
|
|
}
|
|
put_enqueue_action(b, str_to_u32(port_s), str_to_u32(queue));
|
|
} else if (!strcasecmp(act, "drop")) {
|
|
/* A drop action in OpenFlow occurs by just not setting
|
|
* an action. */
|
|
drop = true;
|
|
if (n_actions) {
|
|
ovs_fatal(0, "Drop actions must not be preceded by other "
|
|
"actions");
|
|
}
|
|
} else if (!strcasecmp(act, "CONTROLLER")) {
|
|
struct ofp_action_output *oao;
|
|
oao = put_output_action(b, OFPP_CONTROLLER);
|
|
|
|
/* Unless a numeric argument is specified, we send the whole
|
|
* packet to the controller. */
|
|
if (arg && (strspn(arg, "0123456789") == strlen(arg))) {
|
|
oao->max_len = htons(str_to_u32(arg));
|
|
} else {
|
|
oao->max_len = htons(UINT16_MAX);
|
|
}
|
|
} else if (parse_port_name(act, &port)) {
|
|
put_output_action(b, port);
|
|
} else if (strspn(act, "0123456789") == strlen(act)) {
|
|
put_output_action(b, str_to_u32(act));
|
|
} else {
|
|
ovs_fatal(0, "Unknown action: %s", act);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct protocol {
|
|
const char *name;
|
|
uint16_t dl_type;
|
|
uint8_t nw_proto;
|
|
};
|
|
|
|
static bool
|
|
parse_protocol(const char *name, const struct protocol **p_out)
|
|
{
|
|
static const struct protocol protocols[] = {
|
|
{ "ip", ETH_TYPE_IP, 0 },
|
|
{ "arp", ETH_TYPE_ARP, 0 },
|
|
{ "icmp", ETH_TYPE_IP, IP_TYPE_ICMP },
|
|
{ "tcp", ETH_TYPE_IP, IP_TYPE_TCP },
|
|
{ "udp", ETH_TYPE_IP, IP_TYPE_UDP },
|
|
};
|
|
const struct 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;
|
|
}
|
|
|
|
#define FIELDS \
|
|
FIELD(F_IN_PORT, "in_port", FWW_IN_PORT) \
|
|
FIELD(F_DL_VLAN, "dl_vlan", 0) \
|
|
FIELD(F_DL_VLAN_PCP, "dl_vlan_pcp", 0) \
|
|
FIELD(F_DL_SRC, "dl_src", FWW_DL_SRC) \
|
|
FIELD(F_DL_DST, "dl_dst", FWW_DL_DST) \
|
|
FIELD(F_DL_TYPE, "dl_type", FWW_DL_TYPE) \
|
|
FIELD(F_NW_SRC, "nw_src", 0) \
|
|
FIELD(F_NW_DST, "nw_dst", 0) \
|
|
FIELD(F_NW_PROTO, "nw_proto", FWW_NW_PROTO) \
|
|
FIELD(F_NW_TOS, "nw_tos", FWW_NW_TOS) \
|
|
FIELD(F_TP_SRC, "tp_src", FWW_TP_SRC) \
|
|
FIELD(F_TP_DST, "tp_dst", FWW_TP_DST) \
|
|
FIELD(F_ICMP_TYPE, "icmp_type", FWW_TP_SRC) \
|
|
FIELD(F_ICMP_CODE, "icmp_code", FWW_TP_DST)
|
|
|
|
enum field_index {
|
|
#define FIELD(ENUM, NAME, WILDCARD) ENUM,
|
|
FIELDS
|
|
#undef FIELD
|
|
N_FIELDS
|
|
};
|
|
|
|
struct field {
|
|
enum field_index index;
|
|
const char *name;
|
|
flow_wildcards_t wildcard; /* FWW_* bit. */
|
|
};
|
|
|
|
static bool
|
|
parse_field_name(const char *name, const struct field **f_out)
|
|
{
|
|
static const struct field fields[N_FIELDS] = {
|
|
#define FIELD(ENUM, NAME, WILDCARD) { ENUM, NAME, WILDCARD },
|
|
FIELDS
|
|
#undef FIELD
|
|
};
|
|
const struct field *f;
|
|
|
|
for (f = fields; f < &fields[ARRAY_SIZE(fields)]; f++) {
|
|
if (!strcmp(f->name, name)) {
|
|
*f_out = f;
|
|
return true;
|
|
}
|
|
}
|
|
*f_out = NULL;
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
parse_field_value(struct cls_rule *rule, enum field_index index,
|
|
const char *value)
|
|
{
|
|
uint8_t mac[ETH_ADDR_LEN];
|
|
ovs_be32 ip, mask;
|
|
uint16_t port_no;
|
|
|
|
switch (index) {
|
|
case F_IN_PORT:
|
|
if (!parse_port_name(value, &port_no)) {
|
|
port_no = atoi(value);
|
|
}
|
|
if (port_no == OFPP_LOCAL) {
|
|
port_no = ODPP_LOCAL;
|
|
}
|
|
cls_rule_set_in_port(rule, port_no);
|
|
break;
|
|
|
|
case F_DL_VLAN:
|
|
cls_rule_set_dl_vlan(rule, htons(str_to_u32(value)));
|
|
break;
|
|
|
|
case F_DL_VLAN_PCP:
|
|
cls_rule_set_dl_vlan_pcp(rule, str_to_u32(value));
|
|
break;
|
|
|
|
case F_DL_SRC:
|
|
str_to_mac(value, mac);
|
|
cls_rule_set_dl_src(rule, mac);
|
|
break;
|
|
|
|
case F_DL_DST:
|
|
str_to_mac(value, mac);
|
|
cls_rule_set_dl_dst(rule, mac);
|
|
break;
|
|
|
|
case F_DL_TYPE:
|
|
cls_rule_set_dl_type(rule, htons(str_to_u32(value)));
|
|
break;
|
|
|
|
case F_NW_SRC:
|
|
str_to_ip(value, &ip, &mask);
|
|
cls_rule_set_nw_src_masked(rule, ip, mask);
|
|
break;
|
|
|
|
case F_NW_DST:
|
|
str_to_ip(value, &ip, &mask);
|
|
cls_rule_set_nw_dst_masked(rule, ip, mask);
|
|
break;
|
|
|
|
case F_NW_PROTO:
|
|
cls_rule_set_nw_proto(rule, str_to_u32(value));
|
|
break;
|
|
|
|
case F_NW_TOS:
|
|
cls_rule_set_nw_tos(rule, str_to_u32(value));
|
|
break;
|
|
|
|
case F_TP_SRC:
|
|
cls_rule_set_tp_src(rule, htons(str_to_u32(value)));
|
|
break;
|
|
|
|
case F_TP_DST:
|
|
cls_rule_set_tp_dst(rule, htons(str_to_u32(value)));
|
|
break;
|
|
|
|
case F_ICMP_TYPE:
|
|
cls_rule_set_icmp_type(rule, str_to_u32(value));
|
|
break;
|
|
|
|
case F_ICMP_CODE:
|
|
cls_rule_set_icmp_code(rule, str_to_u32(value));
|
|
break;
|
|
|
|
case N_FIELDS:
|
|
NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
/* Convert 'string' (as described in the Flow Syntax section of the ovs-ofctl
|
|
* man page) into 'pf'. If 'actions' is specified, an action must be in
|
|
* 'string' and may be expanded or reallocated. */
|
|
void
|
|
parse_ofp_str(struct parsed_flow *pf, struct ofpbuf *actions, char *string)
|
|
{
|
|
char *save_ptr = NULL;
|
|
char *name;
|
|
|
|
cls_rule_init_catchall(&pf->rule, OFP_DEFAULT_PRIORITY);
|
|
pf->table_idx = 0xff;
|
|
pf->out_port = OFPP_NONE;
|
|
pf->idle_timeout = OFP_FLOW_PERMANENT;
|
|
pf->hard_timeout = OFP_FLOW_PERMANENT;
|
|
pf->cookie = 0;
|
|
if (actions) {
|
|
char *act_str = strstr(string, "action");
|
|
if (!act_str) {
|
|
ovs_fatal(0, "must specify an action");
|
|
}
|
|
*act_str = '\0';
|
|
|
|
act_str = strchr(act_str + 1, '=');
|
|
if (!act_str) {
|
|
ovs_fatal(0, "must specify an action");
|
|
}
|
|
|
|
act_str++;
|
|
|
|
str_to_action(act_str, actions);
|
|
}
|
|
for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
|
|
name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
|
|
const struct protocol *p;
|
|
|
|
if (parse_protocol(name, &p)) {
|
|
cls_rule_set_dl_type(&pf->rule, htons(p->dl_type));
|
|
if (p->nw_proto) {
|
|
cls_rule_set_nw_proto(&pf->rule, p->nw_proto);
|
|
}
|
|
} else {
|
|
const struct field *f;
|
|
char *value;
|
|
|
|
value = strtok_r(NULL, ", \t\r\n", &save_ptr);
|
|
if (!value) {
|
|
ovs_fatal(0, "field %s missing value", name);
|
|
}
|
|
|
|
if (!strcmp(name, "table")) {
|
|
pf->table_idx = atoi(value);
|
|
} else if (!strcmp(name, "out_port")) {
|
|
pf->out_port = atoi(value);
|
|
} else if (!strcmp(name, "priority")) {
|
|
pf->rule.priority = atoi(value);
|
|
} else if (!strcmp(name, "idle_timeout")) {
|
|
pf->idle_timeout = atoi(value);
|
|
} else if (!strcmp(name, "hard_timeout")) {
|
|
pf->hard_timeout = atoi(value);
|
|
} else if (!strcmp(name, "cookie")) {
|
|
pf->cookie = str_to_u64(value);
|
|
} else if (parse_field_name(name, &f)) {
|
|
if (!strcmp(value, "*") || !strcmp(value, "ANY")) {
|
|
if (f->wildcard) {
|
|
pf->rule.wc.wildcards |= f->wildcard;
|
|
cls_rule_zero_wildcarded_fields(&pf->rule);
|
|
} else if (f->index == F_NW_SRC) {
|
|
cls_rule_set_nw_src_masked(&pf->rule, 0, 0);
|
|
} else if (f->index == F_NW_DST) {
|
|
cls_rule_set_nw_dst_masked(&pf->rule, 0, 0);
|
|
} else if (f->index == F_DL_VLAN) {
|
|
cls_rule_set_any_vid(&pf->rule);
|
|
} else if (f->index == F_DL_VLAN_PCP) {
|
|
cls_rule_set_any_pcp(&pf->rule);
|
|
} else {
|
|
NOT_REACHED();
|
|
}
|
|
} else {
|
|
parse_field_value(&pf->rule, f->index, value);
|
|
}
|
|
} else {
|
|
ovs_fatal(0, "unknown keyword %s", name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Parses 'string' as an OFPT_FLOW_MOD with command 'command' (one of OFPFC_*)
|
|
* and returns an ofpbuf that contains it. */
|
|
struct ofpbuf *
|
|
parse_ofp_flow_mod_str(char *string, uint16_t command)
|
|
{
|
|
struct parsed_flow pf;
|
|
struct ofpbuf *buffer;
|
|
struct ofp_flow_mod *ofm;
|
|
|
|
/* parse_ofp_str() will expand and reallocate the data in 'buffer', so we
|
|
* can't keep pointers to across the parse_ofp_str() call. */
|
|
make_openflow(sizeof *ofm, OFPT_FLOW_MOD, &buffer);
|
|
parse_ofp_str(&pf, buffer, string);
|
|
|
|
ofm = buffer->data;
|
|
ofputil_cls_rule_to_match(&pf.rule, NXFF_OPENFLOW10, &ofm->match);
|
|
ofm->command = htons(command);
|
|
ofm->cookie = htonll(pf.cookie);
|
|
ofm->idle_timeout = htons(pf.idle_timeout);
|
|
ofm->hard_timeout = htons(pf.hard_timeout);
|
|
ofm->buffer_id = htonl(UINT32_MAX);
|
|
ofm->out_port = htons(pf.out_port);
|
|
ofm->priority = htons(pf.rule.priority);
|
|
update_openflow_length(buffer);
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/* Parses an OFPT_FLOW_MOD with subtype OFPFC_ADD from 'stream' and returns an
|
|
* ofpbuf that contains it. Returns a null pointer if end-of-file is reached
|
|
* before reading a flow. */
|
|
struct ofpbuf *
|
|
parse_ofp_add_flow_file(FILE *stream)
|
|
{
|
|
struct ofpbuf *b = NULL;
|
|
struct ds s = DS_EMPTY_INITIALIZER;
|
|
|
|
while (!ds_get_line(&s, stream)) {
|
|
char *line = ds_cstr(&s);
|
|
char *comment;
|
|
|
|
/* Delete comments. */
|
|
comment = strchr(line, '#');
|
|
if (comment) {
|
|
*comment = '\0';
|
|
}
|
|
|
|
/* Drop empty lines. */
|
|
if (line[strspn(line, " \t\n")] == '\0') {
|
|
continue;
|
|
}
|
|
|
|
b = parse_ofp_flow_mod_str(line, OFPFC_ADD);
|
|
break;
|
|
}
|
|
ds_destroy(&s);
|
|
|
|
return b;
|
|
}
|