+int
+ofproto_dpif_add_internal_flow(struct ofproto_dpif *ofproto,
+ struct match *match, int priority,
+ const struct ofpbuf *ofpacts,
+ struct rule **rulep)
+{
+ struct ofputil_flow_mod fm;
+ struct rule_dpif *rule;
+ int error;
+
+ fm.match = *match;
+ fm.priority = priority;
+ fm.new_cookie = htonll(0);
+ fm.cookie = htonll(0);
+ fm.cookie_mask = htonll(0);
+ fm.modify_cookie = false;
+ fm.table_id = TBL_INTERNAL;
+ fm.command = OFPFC_ADD;
+ fm.idle_timeout = 0;
+ fm.hard_timeout = 0;
+ fm.buffer_id = 0;
+ fm.out_port = 0;
+ fm.flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY;
+ fm.ofpacts = ofpbuf_data(ofpacts);
+ fm.ofpacts_len = ofpbuf_size(ofpacts);
+
+ error = ofproto_flow_mod(&ofproto->up, &fm);
+ if (error) {
+ VLOG_ERR_RL(&rl, "failed to add internal flow (%s)",
+ ofperr_to_string(error));
+ *rulep = NULL;
+ return error;
+ }
+
+ rule = rule_dpif_lookup_in_table(ofproto, TBL_INTERNAL, &match->flow,
+ &match->wc);
+ if (rule) {
+ rule_dpif_unref(rule);
+ *rulep = &rule->up;
+ } else {
+ OVS_NOT_REACHED();
+ }
+ return 0;
+}
+
+int
+ofproto_dpif_delete_internal_flow(struct ofproto_dpif *ofproto,
+ struct match *match, int priority)
+{
+ struct ofputil_flow_mod fm;
+ int error;
+
+ fm.match = *match;
+ fm.priority = priority;
+ fm.new_cookie = htonll(0);
+ fm.cookie = htonll(0);
+ fm.cookie_mask = htonll(0);
+ fm.modify_cookie = false;
+ fm.table_id = TBL_INTERNAL;
+ fm.flags = OFPUTIL_FF_HIDDEN_FIELDS | OFPUTIL_FF_NO_READONLY;
+ fm.command = OFPFC_DELETE_STRICT;
+
+ error = ofproto_flow_mod(&ofproto->up, &fm);
+ if (error) {
+ VLOG_ERR_RL(&rl, "failed to delete internal flow (%s)",
+ ofperr_to_string(error));
+ return error;
+ }
+
+ return 0;
+}
+