Skip to content

Commit

Permalink
flow: Factor out flag parsing and formatting routines.
Browse files Browse the repository at this point in the history
There are several implementations of functions that parse/format
flags and their binary representation. This factors them out into
common routines. In addition to reducing code, it also makes things
more consistent across different parts of OVS.

Signed-off-by: Jesse Gross <[email protected]>
  • Loading branch information
jessegross committed Jul 16, 2015
1 parent cadf7ec commit 8e4c162
Show file tree
Hide file tree
Showing 9 changed files with 255 additions and 312 deletions.
173 changes: 172 additions & 1 deletion lib/flow.c
Original file line number Diff line number Diff line change
Expand Up @@ -838,6 +838,7 @@ format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
uint32_t bad = 0;

if (!flags) {
ds_put_char(ds, '0');
return;
}
while (flags) {
Expand All @@ -863,11 +864,22 @@ format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
void
format_flags_masked(struct ds *ds, const char *name,
const char *(*bit_to_string)(uint32_t), uint32_t flags,
uint32_t mask)
uint32_t mask, uint32_t max_mask)
{
if (name) {
ds_put_format(ds, "%s=", name);
}

if (mask == max_mask) {
format_flags(ds, bit_to_string, flags, '|');
return;
}

if (!mask) {
ds_put_cstr(ds, "0/0");
return;
}

while (mask) {
uint32_t bit = rightmost_1bit(mask);
const char *s = bit_to_string(bit);
Expand All @@ -878,6 +890,165 @@ format_flags_masked(struct ds *ds, const char *name,
}
}

/* Scans a string 's' of flags to determine their numerical value and
* returns the number of characters parsed using 'bit_to_string' to
* lookup flag names. Scanning continues until the character 'end' is
* reached.
*
* In the event of a failure, a negative error code will be returned. In
* addition, if 'res_string' is non-NULL then a descriptive string will
* be returned incorporating the identifying string 'field_name'. This
* error string must be freed by the caller.
*
* Upon success, the flag values will be stored in 'res_flags' and
* optionally 'res_mask', if it is non-NULL (if it is NULL then any masks
* present in the original string will be considered an error). The
* caller may restrict the acceptable set of values through the mask
* 'allowed'. */
int
parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
char end, const char *field_name, char **res_string,
uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask)
{
uint32_t result = 0;
int n;

/* Parse masked flags in numeric format? */
if (res_mask && ovs_scan(s, "%"SCNi32"/%"SCNi32"%n",
res_flags, res_mask, &n) && n > 0) {
if (*res_flags & ~allowed || *res_mask & ~allowed) {
goto unknown;
}
return n;
}

n = 0;

if (res_mask && (*s == '+' || *s == '-')) {
uint32_t flags = 0, mask = 0;

/* Parse masked flags. */
while (s[0] != end) {
bool set;
uint32_t bit;
size_t len;

if (s[0] == '+') {
set = true;
} else if (s[0] == '-') {
set = false;
} else {
if (res_string) {
*res_string = xasprintf("%s: %s must be preceded by '+' "
"(for SET) or '-' (NOT SET)", s,
field_name);
}
return -EINVAL;
}
s++;
n++;

for (bit = 1; bit; bit <<= 1) {
const char *fname = bit_to_string(bit);

if (!fname) {
continue;
}

len = strlen(fname);
if (strncmp(s, fname, len) ||
(s[len] != '+' && s[len] != '-' && s[len] != end)) {
continue;
}

if (mask & bit) {
/* bit already set. */
if (res_string) {
*res_string = xasprintf("%s: Each %s flag can be "
"specified only once", s,
field_name);
}
return -EINVAL;
}
if (!(bit & allowed)) {
goto unknown;
}
if (set) {
flags |= bit;
}
mask |= bit;
break;
}

if (!bit) {
goto unknown;
}
s += len;
n += len;
}

*res_flags = flags;
*res_mask = mask;
return n;
}

/* Parse unmasked flags. If a flag is present, it is set, otherwise
* it is not set. */
while (s[n] != end) {
unsigned long long int flags;
uint32_t bit;
int n0;

if (ovs_scan(&s[n], "%lli%n", &flags, &n0)) {
if (flags & ~allowed) {
goto unknown;
}
n += n0 + (s[n + n0] == '|');
result |= flags;
continue;
}

for (bit = 1; bit; bit <<= 1) {
const char *name = bit_to_string(bit);
size_t len;

if (!name) {
continue;
}

len = strlen(name);
if (!strncmp(s + n, name, len) &&
(s[n + len] == '|' || s[n + len] == end)) {
if (!(bit & allowed)) {
goto unknown;
}
result |= bit;
n += len + (s[n + len] == '|');
break;
}
}

if (!bit) {
goto unknown;
}
}

*res_flags = result;
if (res_mask) {
*res_mask = UINT32_MAX;
}
if (res_string) {
*res_string = NULL;
}
return n;

unknown:
if (res_string) {
*res_string = xasprintf("%s: unknown %s flag(s)", s, field_name);
}
return -EINVAL;
}

void
flow_format(struct ds *ds, const struct flow *flow)
{
Expand Down
5 changes: 4 additions & 1 deletion lib/flow.h
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,10 @@ void format_flags(struct ds *ds, const char *(*bit_to_string)(uint32_t),
uint32_t flags, char del);
void format_flags_masked(struct ds *ds, const char *name,
const char *(*bit_to_string)(uint32_t),
uint32_t flags, uint32_t mask);
uint32_t flags, uint32_t mask, uint32_t max_mask);
int parse_flags(const char *s, const char *(*bit_to_string)(uint32_t),
char end, const char *field_name, char **res_string,
uint32_t *res_flags, uint32_t allowed, uint32_t *res_mask);

void flow_format(struct ds *, const struct flow *);
void flow_print(FILE *, const struct flow *);
Expand Down
17 changes: 3 additions & 14 deletions lib/match.c
Original file line number Diff line number Diff line change
Expand Up @@ -1150,20 +1150,9 @@ match_format(const struct match *match, struct ds *s, int priority)
format_be16_masked(s, "tp_dst", f->tp_dst, wc->masks.tp_dst);
}
if (is_ip_any(f) && f->nw_proto == IPPROTO_TCP && wc->masks.tcp_flags) {
uint16_t mask = TCP_FLAGS(wc->masks.tcp_flags);

if (mask == TCP_FLAGS(OVS_BE16_MAX)) {
ds_put_cstr(s, "tcp_flags=");
if (f->tcp_flags) {
format_flags(s, packet_tcp_flag_to_string, ntohs(f->tcp_flags),
'|');
} else {
ds_put_cstr(s, "0"); /* Zero flags. */
}
} else if (mask) {
format_flags_masked(s, "tcp_flags", packet_tcp_flag_to_string,
ntohs(f->tcp_flags), mask);
}
format_flags_masked(s, "tcp_flags", packet_tcp_flag_to_string,
ntohs(f->tcp_flags), TCP_FLAGS(wc->masks.tcp_flags),
TCP_FLAGS(OVS_BE16_MAX));
}

if (s->length > start_len) {
Expand Down
Loading

0 comments on commit 8e4c162

Please sign in to comment.