+ "\"csum\", \"key\")", s);
+}
+
+static char *
+mf_from_tcp_flags_string(const char *s, ovs_be16 *flagsp, ovs_be16 *maskp)
+{
+ uint16_t flags = 0;
+ uint16_t mask = 0;
+ uint16_t bit;
+ int n;
+
+ if (ovs_scan(s, "%"SCNi16"/%"SCNi16"%n", &flags, &mask, &n) && !s[n]) {
+ *flagsp = htons(flags);
+ *maskp = htons(mask);
+ return NULL;
+ }
+ if (ovs_scan(s, "%"SCNi16"%n", &flags, &n) && !s[n]) {
+ *flagsp = htons(flags);
+ *maskp = OVS_BE16_MAX;
+ return NULL;
+ }
+
+ while (*s != '\0') {
+ bool set;
+ int name_len;
+
+ switch (*s) {
+ case '+':
+ set = true;
+ break;
+ case '-':
+ set = false;
+ break;
+ default:
+ return xasprintf("%s: TCP flag must be preceded by '+' (for SET) "
+ "or '-' (NOT SET)", s);
+ }
+ s++;
+
+ name_len = strcspn(s,"+-");
+
+ for (bit = 1; bit; bit <<= 1) {
+ const char *fname = packet_tcp_flag_to_string(bit);
+ size_t len;
+
+ if (!fname) {
+ continue;
+ }
+
+ len = strlen(fname);
+ if (len != name_len) {
+ continue;
+ }
+ if (!strncmp(s, fname, len)) {
+ if (mask & bit) {
+ return xasprintf("%s: Each TCP flag can be specified only "
+ "once", s);
+ }
+ if (set) {
+ flags |= bit;
+ }
+ mask |= bit;
+ break;
+ }
+ }
+
+ if (!bit) {
+ return xasprintf("%s: unknown TCP flag(s)", s);
+ }
+ s += name_len;
+ }
+
+ *flagsp = htons(flags);
+ *maskp = htons(mask);
+ return NULL;