Ejemplo n.º 1
0
 def get_const_matches(self, ctx, c_keys):
     matches = []
     # match the constant keys with the normal table keys
     # this generates the match expression for a specific constant entry
     # this is a little inefficient, FIXME.
     for index, (key_expr, _) in enumerate(self.keys):
         c_key_expr = c_keys[index]
         # default implies don't care, do not add
         # TODO: Verify that this assumption is right...
         if isinstance(c_key_expr, DefaultExpression):
             continue
         # TODO: Unclear about the role of side-effects here
         key_eval = ctx.resolve_expr(key_expr)
         if isinstance(c_key_expr, P4Range):
             x = ctx.resolve_expr(c_key_expr.min)
             y = ctx.resolve_expr(c_key_expr.max)
             c_key_eval = z3.And(z3.ULE(x, key_eval), z3.UGE(y, key_eval))
             matches.append(c_key_eval)
         elif isinstance(c_key_expr, P4Mask):
             # TODO: Unclear about the role of side-effects here
             val = ctx.resolve_expr(c_key_expr.value)
             mask = ctx.resolve_expr(c_key_expr.mask)
             c_key_eval = (val & mask) == (key_eval & mask)
             matches.append(c_key_eval)
         else:
             c_key_eval = ctx.resolve_expr(c_key_expr)
             matches.append(key_eval == c_key_eval)
     return z3.And(*matches)
Ejemplo n.º 2
0
    def eval(self, ctx):
        cond = z3.simplify(ctx.resolve_expr(self.cond))
        forward_cond_copy = ctx.tmp_forward_cond
        then_vars = None
        if not z3.is_false(cond):
            var_store = ctx.checkpoint()
            ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
            try:
                self.then_block.eval(ctx)
            except ParserException:
                RejectState().eval(ctx)
            if not(ctx.has_returned or ctx.get_exited()):
                then_vars = ctx.get_attrs()
            ctx.set_exited(False)
            ctx.has_returned = False
            ctx.restore(var_store)

        if not z3.is_true(cond):
            var_store = ctx.checkpoint()
            ctx.tmp_forward_cond = z3.And(forward_cond_copy, z3.Not(cond))
            try:
                self.else_block.eval(ctx)
            except ParserException:
                RejectState().eval(ctx)
            if ctx.get_exited() or ctx.has_returned:
                ctx.restore(var_store)
            ctx.set_exited(False)
            ctx.has_returned = False

        ctx.tmp_forward_cond = forward_cond_copy

        if then_vars:
            merge_attrs(ctx, cond, then_vars)
Ejemplo n.º 3
0
 def eval_keys(self, ctx):
     key_pairs = []
     if not self.keys:
         # there is nothing to match with...
         return z3.BoolVal(False)
     for index, (key_expr, key_type) in enumerate(self.keys):
         key_eval = ctx.resolve_expr(key_expr)
         key_sort = key_eval.sort()
         key_match = z3.Const(f"{self.name}_table_key_{index}", key_sort)
         if key_type == "exact":
             # Just a simple comparison, nothing special
             key_pairs.append(key_eval == key_match)
         elif key_type == "lpm":
             # I think this can be arbitrarily large...
             # If the shift exceeds the bit width, everything will be zero
             # but that does not matter
             # TODO: Test this?
             mask_var = z3.BitVec(f"{self.name}_table_mask_{index}",
                                  key_sort)
             lpm_mask = z3.BitVecVal(2**key_sort.size() - 1,
                                     key_sort) << mask_var
             match = (key_eval & lpm_mask) == (key_match & lpm_mask)
             key_pairs.append(match)
         elif key_type == "ternary":
             # Just apply a symbolic mask, any zero bit is a wildcard
             # TODO: Test this?
             mask = z3.Const(f"{self.name}_table_mask_{index}", key_sort)
             # this is dumb...
             if isinstance(key_sort, z3.BoolSortRef):
                 match = z3.And(key_eval, mask) == z3.And(key_match, mask)
             else:
                 match = (key_eval & mask) == (key_match & mask)
             key_pairs.append(match)
         elif key_type == "range":
             # Pick an arbitrary minimum and maximum within the bit range
             # the minimum must be strictly lesser than the max
             # I do not think a match is needed?
             # TODO: Test this?
             min_key = z3.Const(f"{self.name}_table_min_{index}", key_sort)
             max_key = z3.Const(f"{self.name}_table_max_{index}", key_sort)
             match = z3.And(z3.ULE(min_key, key_eval),
                            z3.UGE(max_key, key_eval))
             key_pairs.append(z3.And(match, z3.ULT(min_key, max_key)))
         elif key_type == "optional":
             # As far as I understand this is just a wildcard for control
             # plane purposes. Semantically, there is no point?
             # TODO: Test this?
             key_pairs.append(z3.BoolVal(True))
         elif key_type == "selector":
             # Selectors are a deep rabbit hole
             # This rabbit hole does not yet make sense to me
             # FIXME: Implement
             # will intentionally fail if no implementation is present
             # impl = self.properties["implementation"]
             # impl_extern = self.prog_state.resolve_reference(impl)
             key_pairs.append(z3.BoolVal(True))
         else:
             # weird key, might be some specific specification
             raise RuntimeError(f"Key type {key_type} not supported!")
     return z3.And(key_pairs)
Ejemplo n.º 4
0
    def eval(self, p4_state):
        if self.is_terminal:
            context = p4_state.current_context()
            key = self.parser_state.name
            tmp_forward_conds = []
            for context in reversed(p4_state.contexts):
                tmp_forward_conds.append(context.tmp_forward_cond)
            cond = z3.And(*tmp_forward_conds)
            attrs = p4_state.get_attrs()
            # add the
            if key in self.parser_tree.terminal_nodes:
                orig_cond = self.parser_tree.terminal_nodes[key][0]
                orig_dict = self.parser_tree.terminal_nodes[key][1]
                merge_dicts(orig_dict, cond, attrs)
                cond = z3.And(orig_cond, cond)
                attrs = orig_dict
            self.parser_tree.terminal_nodes[key] = (cond, attrs)
            return

        parser_state = self.parser_state
        try:
            parser_state.eval(p4_state)
            if isinstance(self.child, list):
                # there is a switch case try to untangle it.
                self.handle_select(p4_state)
            elif isinstance(self.child, ParserNode):
                # direct descendant, continue the evaluation
                self.child.eval(p4_state)
        except ParserException:
            RejectState().eval(p4_state)
Ejemplo n.º 5
0
    def handle_select(self, ctx):
        switches = []
        select_conds = []
        forward_cond_copy = ctx.tmp_forward_cond
        match_list = ctx.resolve_expr(self.match)
        for parser_cond, parser_node in self.child:
            case_expr = ctx.resolve_expr(parser_cond)
            cond = build_select_cond(ctx, case_expr, match_list)
            # state forks here
            var_store = ctx.checkpoint()
            ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
            parser_node.eval(ctx)
            select_conds.append(cond)
            if not (ctx.get_exited() or z3.is_false(cond)):
                switches.append((ctx.tmp_forward_cond, ctx.get_attrs()))
            ctx.set_exited(False)
            ctx.has_returned = False
            ctx.restore(var_store)

        # this hits when no select matches
        cond = z3.Not(z3.Or(*select_conds))
        ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
        self.default.eval(ctx)
        ctx.set_exited(False)
        ctx.has_returned = False
        ctx.tmp_forward_cond = forward_cond_copy
        for cond, then_vars in reversed(switches):
            merge_attrs(ctx, cond, then_vars)
Ejemplo n.º 6
0
    def handle_select(self, p4_state):
        switches = []
        select_conds = []
        context = p4_state.current_context()
        forward_cond_copy = context.tmp_forward_cond
        match_list = p4_state.resolve_expr(self.match)
        for parser_cond, parser_node in reversed(self.child):
            case_expr = p4_state.resolve_expr(parser_cond)
            cond = build_select_cond(p4_state, case_expr, match_list)
            # state forks here
            var_store, contexts = p4_state.checkpoint()
            context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
            parser_node.eval(p4_state)
            select_conds.append(cond)
            if not p4_state.has_exited:
                switches.append(
                    (context.tmp_forward_cond, p4_state.get_attrs()))
            p4_state.has_exited = False
            context.has_returned = False
            p4_state.restore(var_store, contexts)

        # this hits when the table is either missed, or no action matches
        cond = z3.Not(z3.Or(*select_conds))
        context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
        self.default.eval(p4_state)
        p4_state.has_exited = False
        context.has_returned = False
        context.tmp_forward_cond = forward_cond_copy
        for cond, then_vars in switches:
            merge_attrs(p4_state, cond, then_vars)
Ejemplo n.º 7
0
def build_select_cond(p4_state, case_expr, match_list):
    select_cond = []
    # these casts are kind of silly but simplify the code a lot
    if isinstance(case_expr, StructInstance):
        case_expr = case_expr.flatten()
    elif not isinstance(case_expr, list):
        case_expr = [case_expr]

    for idx, case_match in enumerate(case_expr):
        # default implies don't care, do not add
        # TODO: Verify that this assumption is right...
        if isinstance(case_match, DefaultExpression):
            select_cond.append(z3.BoolVal(True))
        elif isinstance(case_match, P4Range):
            x = case_match.min
            y = case_match.max
            match_key = z3.And(z3.ULE(x, match_list[idx]),
                               z3.UGE(y, match_list[idx]))
            select_cond.append(match_key)
        elif isinstance(case_match, P4Mask):
            val = p4_state.resolve_expr(case_match.value)
            mask = case_match.mask
            match_key = (val | ~mask) == (match_list[idx] | ~mask)
            select_cond.append(match_key)
        else:
            select_cond.append(case_match == match_list[idx])
    if not select_cond:
        return z3.BoolVal(False)
    return z3.And(*select_cond)
Ejemplo n.º 8
0
    def eval(self, p4_state):
        context = p4_state.current_context()
        cond = z3.simplify(p4_state.resolve_expr(self.cond))
        forward_cond_copy = context.tmp_forward_cond
        then_vars = None
        if not cond == z3.BoolVal(False):
            var_store, contexts = p4_state.checkpoint()
            context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
            try:
                self.then_block.eval(p4_state)
            except ParserException:
                RejectState().eval(p4_state)
            if not (context.has_returned or p4_state.has_exited):
                then_vars = p4_state.get_attrs()
            p4_state.has_exited = False
            context.has_returned = False
            p4_state.restore(var_store, contexts)

        if not cond == z3.BoolVal(True):
            var_store, contexts = p4_state.checkpoint()
            context.tmp_forward_cond = z3.And(forward_cond_copy, z3.Not(cond))
            try:
                self.else_block.eval(p4_state)
            except ParserException:
                RejectState().eval(p4_state)
            if p4_state.has_exited or context.has_returned:
                p4_state.restore(var_store, contexts)
            p4_state.has_exited = False
            context.has_returned = False

        context.tmp_forward_cond = forward_cond_copy

        if then_vars:
            merge_attrs(p4_state, cond, then_vars)
Ejemplo n.º 9
0
 def eval_table_entries(self, ctx, action_exprs, action_matches):
     forward_cond_copy = ctx.tmp_forward_cond
     for act_id, act_name, act_args in reversed(self.actions.values()):
         action_match = (self.tbl_action == z3.IntVal(act_id))
         log.debug("Evaluating action %s...", act_name)
         # state forks here
         var_store = ctx.checkpoint()
         cond = z3.And(self.locals["hit"], action_match)
         ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
         self.eval_action(ctx, act_name, act_args, act_id)
         if not ctx.get_exited():
             action_exprs.append((cond, ctx.get_attrs()))
         ctx.set_exited(False)
         action_matches.append(action_match)
         ctx.restore(var_store)
Ejemplo n.º 10
0
    def eval_table(self, p4_state):
        action_exprs = []
        action_matches = []
        context = p4_state.current_context()
        forward_cond_copy = context.tmp_forward_cond

        # only bother to evaluate if the table can actually hit
        if not self.locals["hit"] == z3.BoolVal(False):
            # note: the action lists are pass by reference here
            # first evaluate all the constant entries
            self.eval_const_entries(p4_state, action_exprs, action_matches)
            # then append dynamic table entries to the constant entries
            self.eval_table_entries(p4_state, action_exprs, action_matches)
        # finally start evaluating the default entry
        var_store, contexts = p4_state.checkpoint()
        # this hits when the table is either missed, or no action matches
        cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches)))
        context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
        self.eval_default(p4_state)
        if p4_state.has_exited:
            p4_state.restore(var_store, contexts)
        p4_state.has_exited = False
        context.tmp_forward_cond = forward_cond_copy
        # generate a nested set of if expressions per available action
        for cond, then_vars in action_exprs:
            merge_attrs(p4_state, cond, then_vars)
Ejemplo n.º 11
0
 def eval_table_entries(self, p4_state, action_exprs, action_matches):
     context = p4_state.current_context()
     forward_cond_copy = context.tmp_forward_cond
     for act_id, act_name, act_args in reversed(self.actions.values()):
         action_match = (self.tbl_action == z3.IntVal(act_id))
         log.debug("Evaluating action %s...", act_name)
         # state forks here
         var_store, contexts = p4_state.checkpoint()
         cond = z3.And(self.locals["hit"], action_match)
         context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
         self.eval_action(p4_state, act_name, act_args)
         if not p4_state.has_exited:
             action_exprs.append((cond, p4_state.get_attrs()))
         p4_state.has_exited = False
         action_matches.append(action_match)
         p4_state.restore(var_store, contexts)
Ejemplo n.º 12
0
    def eval_const_entries(self, ctx, action_exprs, action_matches):
        forward_cond_copy = ctx.tmp_forward_cond
        for c_keys, (action_name, action_args) in reversed(self.const_entries):

            action_match = self.get_const_matches(ctx, c_keys)
            log.debug("Evaluating constant action %s...", action_name)
            # state forks here
            var_store = ctx.checkpoint()
            cond = z3.And(self.locals["hit"], action_match)
            ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
            self.eval_action(ctx, action_name, action_args)
            if not ctx.get_exited():
                action_exprs.append((cond, ctx.get_attrs()))
            ctx.set_exited(False)
            action_matches.append(action_match)
            ctx.restore(var_store)
Ejemplo n.º 13
0
 def eval_cases(self, p4_state, cases):
     case_exprs = []
     case_matches = []
     context = p4_state.current_context()
     forward_cond_copy = context.tmp_forward_cond
     for case in reversed(cases.values()):
         var_store, contexts = p4_state.checkpoint()
         context.tmp_forward_cond = z3.And(forward_cond_copy, case["match"])
         case["case_block"].eval(p4_state)
         if not (context.has_returned or p4_state.has_exited):
             then_vars = p4_state.get_attrs()
             case_exprs.append((case["match"], then_vars))
         context.has_returned = False
         p4_state.has_exited = False
         p4_state.restore(var_store, contexts)
         case_matches.append(case["match"])
     var_store, contexts = p4_state.checkpoint()
     cond = z3.Not(z3.Or(*case_matches))
     context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
     self.default_case.eval(p4_state)
     if context.has_returned or p4_state.has_exited:
         p4_state.restore(var_store, contexts)
     context.has_returned = False
     p4_state.has_exited = False
     context.tmp_forward_cond = forward_cond_copy
     for cond, then_vars in case_exprs:
         merge_attrs(p4_state, cond, then_vars)
Ejemplo n.º 14
0
    def eval_table(self, ctx):
        action_exprs = []
        action_matches = []
        forward_cond_copy = ctx.tmp_forward_cond

        # only bother to evaluate if the table can actually hit
        if not z3.is_false(self.locals["hit"]):
            # note: the action lists are pass by reference here
            # first evaluate all the constant entries
            self.eval_const_entries(ctx, action_exprs, action_matches)
            # then append dynamic table entries to the constant entries
            # only do this if the table can actually be manipulated
            if not self.immutable:
                self.eval_table_entries(ctx, action_exprs, action_matches)
        # finally start evaluating the default entry
        var_store = ctx.checkpoint()
        # this hits when the table is either missed, or no action matches
        cond = z3.Or(self.locals["miss"], z3.Not(z3.Or(*action_matches)))
        ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
        self.eval_default(ctx)
        if ctx.get_exited():
            ctx.restore(var_store)
        ctx.set_exited(False)
        ctx.tmp_forward_cond = forward_cond_copy
        # generate a nested set of if expressions per available action
        for cond, then_vars in action_exprs:
            merge_attrs(ctx, cond, then_vars)
Ejemplo n.º 15
0
    def eval(self, p4_state):
        context = p4_state.current_context()

        if self.expr is None:
            expr = None
        else:
            # resolve the expr before restoring the state
            expr = p4_state.resolve_expr(self.expr)
            if isinstance(context.return_type, z3.BitVecSortRef):
                expr = z3_cast(expr, context.return_type)
            # we return a complex typed expression list, instantiate
            if isinstance(expr, list):
                instance = gen_instance(p4_state, "undefined",
                                        context.return_type)
                instance.set_list(expr)
                expr = instance

        cond = z3.simplify(
            z3.And(z3.Not(z3.Or(*context.forward_conds)),
                   context.tmp_forward_cond))
        if not cond == z3.BoolVal(False):
            context.return_states.append((cond, p4_state.copy_attrs()))
            if expr is not None:
                context.return_exprs.append((cond, expr))
            context.has_returned = True
        context.forward_conds.append(context.tmp_forward_cond)
Ejemplo n.º 16
0
    def eval(self, ctx):

        parser_state = self.parser_state
        try:
            parser_state.eval(ctx)
        except ParserException:
            RejectState().eval(ctx)
            return

        if self.is_terminal:
            key = self.parser_state.name
            tmp_forward_conds = []
            sub_ctx = ctx
            while not isinstance(sub_ctx, StaticContext):
                tmp_forward_conds.append(sub_ctx.tmp_forward_cond)
                sub_ctx = sub_ctx.parent_ctx

            cond = z3.And(*tmp_forward_conds)
            attrs = ctx.get_attrs()
            # add the
            if key in self.parser_tree.terminal_nodes:
                orig_cond = self.parser_tree.terminal_nodes[key][0]
                orig_dict = self.parser_tree.terminal_nodes[key][1]
                merge_dicts(orig_dict, cond, attrs)
                cond = z3.Or(orig_cond, cond)
                attrs = orig_dict
            self.parser_tree.terminal_nodes[key] = (cond, attrs)
            return

        if isinstance(self.child, list):
            # there is a switch case try to untangle it.
            self.handle_select(ctx)
        elif isinstance(self.child, ParserNode):
            # direct descendant, continue the evaluation
            self.child.eval(ctx)
Ejemplo n.º 17
0
 def eval(self, ctx):
     cond = z3.simplify(
         z3.And(z3.Not(z3.Or(*ctx.forward_conds)), ctx.tmp_forward_cond))
     if not z3.is_false(cond):
         ctx.return_states.append((cond, ctx.copy_attrs()))
         ctx.has_returned = True
     ctx.forward_conds.append(ctx.tmp_forward_cond)
Ejemplo n.º 18
0
    def eval(self, p4_state):
        # FIXME: This checkpointing should not be necessary
        # Figure out what is going on
        var_store, contexts = p4_state.checkpoint()
        forward_conds = []
        tmp_forward_conds = []
        for context in reversed(p4_state.contexts):
            context.copy_out(p4_state)
            forward_conds.extend(context.forward_conds)
            tmp_forward_conds.append(context.tmp_forward_cond)
        context = p4_state.current_context()

        cond = z3.simplify(
            z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds)))
        if not cond == z3.BoolVal(False):
            p4_state.exit_states.append((cond, p4_state.get_z3_repr()))
            p4_state.has_exited = True
        p4_state.restore(var_store, contexts)
        context.forward_conds.append(context.tmp_forward_cond)
Ejemplo n.º 19
0
 def eval_const_entries(self, p4_state, action_exprs, action_matches):
     context = p4_state.current_context()
     forward_cond_copy = context.tmp_forward_cond
     for c_keys, (action_name, action_args) in reversed(self.const_entries):
         matches = []
         # match the constant keys with the normal table keys
         # this generates the match expression for a specific constant entry
         # this is a little inefficient, fix.
         # TODO: Figure out if key type matters here?
         for index, (key_expr, key_type) in enumerate(self.keys):
             c_key_expr = c_keys[index]
             # default implies don't care, do not add
             # TODO: Verify that this assumption is right...
             if isinstance(c_key_expr, DefaultExpression):
                 continue
             key_eval = p4_state.resolve_expr(key_expr)
             if isinstance(c_key_expr, P4Range):
                 x = c_key_expr.min
                 y = c_key_expr.max
                 c_key_eval = z3.And(z3.ULE(x, key_eval),
                                     z3.UGE(y, key_eval))
                 matches.append(c_key_eval)
             elif isinstance(c_key_expr, P4Mask):
                 val = p4_state.resolve_expr(c_key_expr.mask)
                 mask = c_key_expr.mask
                 c_key_eval = (val & mask) == (key_eval & mask)
                 matches.append(c_key_eval)
             else:
                 c_key_eval = p4_state.resolve_expr(c_key_expr)
                 matches.append(key_eval == c_key_eval)
         action_match = z3.And(*matches)
         log.debug("Evaluating constant action %s...", action_name)
         # state forks here
         var_store, contexts = p4_state.checkpoint()
         cond = z3.And(self.locals["hit"], action_match)
         context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
         self.eval_action(p4_state, action_name, action_args)
         if not p4_state.has_exited:
             action_exprs.append((cond, p4_state.get_attrs()))
         p4_state.has_exited = False
         action_matches.append(action_match)
         p4_state.restore(var_store, contexts)
Ejemplo n.º 20
0
 def eval_keys(self, p4_state):
     key_pairs = []
     if not self.keys:
         # there is nothing to match with...
         return z3.BoolVal(False)
     for index, key in enumerate(self.keys):
         key_eval = p4_state.resolve_expr(key)
         key_sort = key_eval.sort()
         key_match = z3.Const(f"{self.name}_table_key_{index}", key_sort)
         key_pairs.append(key_eval == key_match)
     return z3.And(key_pairs)
Ejemplo n.º 21
0
    def eval(self, ctx):
        # FIXME: This checkpointing should not be necessary
        # Figure out what is going on
        var_store = ctx.checkpoint()
        forward_conds = []
        tmp_forward_conds = []
        sub_ctx = ctx
        while not isinstance(sub_ctx, StaticContext):
            sub_ctx.copy_out(ctx)
            forward_conds.extend(sub_ctx.forward_conds)
            tmp_forward_conds.append(sub_ctx.tmp_forward_cond)
            sub_ctx = sub_ctx.parent_ctx

        cond = z3.simplify(
            z3.And(z3.Not(z3.Or(*forward_conds)), z3.And(*tmp_forward_conds)))
        if not z3.is_false(cond):
            ctx.add_exit_state(cond, ctx.get_p4_state().get_members(ctx))
            ctx.set_exited(True)
        ctx.restore(var_store)
        ctx.forward_conds.append(ctx.tmp_forward_cond)
Ejemplo n.º 22
0
    def eval_table(self, p4_state):
        actions = self.actions
        const_entries = self.const_entries
        action_exprs = []
        # first evaluate all the constant entries
        for const_keys, action in reversed(const_entries):
            action_name = action[0]
            p4_action_args = action[1]
            matches = []
            # match the constant keys with the normal table keys
            # this generates the match expression for a specific constant entry
            for index, key in enumerate(self.keys):
                key_eval = p4_state.resolve_expr(key)
                const_key = const_keys[index]
                # default implies don't care, do not add
                # TODO: Verify that this assumption is right...
                if isinstance(const_key, DefaultExpression):
                    continue
                c_key_eval = p4_state.resolve_expr(const_keys[index])
                matches.append(key_eval == c_key_eval)
            action_match = z3.And(*matches)
            action_tuple = (action_name, p4_action_args)
            log.debug("Evaluating constant action %s...", action_name)
            # state forks here
            var_store, chain_copy = p4_state.checkpoint()
            action_expr = self.eval_action(p4_state, action_tuple)
            p4_state.restore(var_store, chain_copy)
            action_exprs.append((action_match, action_expr))

        # then append dynamic table entries to the constant entries
        for action in reversed(actions.values()):
            p4_action_id = action[0]
            action_name = action[1]
            p4_action_args = action[2]
            action_match = (self.tbl_action == z3.IntVal(p4_action_id))
            action_tuple = (action_name, p4_action_args)
            log.debug("Evaluating action %s...", action_name)
            # state forks here
            var_store, chain_copy = p4_state.checkpoint()
            action_expr = self.eval_action(p4_state, action_tuple)
            p4_state.restore(var_store, chain_copy)
            action_exprs.append((action_match, action_expr))

        # finally evaluate the default entry
        table_expr = self.eval_default(p4_state)
        default_expr = table_expr
        # generate a nested set of if expressions per available action
        for cond, action_expr in action_exprs:
            table_expr = z3.If(cond, action_expr, table_expr)
        # if we hit return the table expr
        # otherwise just return the default expr
        return z3.If(self.p4_attrs["hit"], table_expr, default_expr)
Ejemplo n.º 23
0
    def eval(self, p4_state):

        # FIXME: This checkpointing should not be necessary
        # Figure out what is going on
        forward_conds = []
        tmp_forward_conds = []
        for context in reversed(p4_state.contexts):
            forward_conds.extend(context.forward_conds)
            tmp_forward_conds.append(context.tmp_forward_cond)
        context = p4_state.current_context()

        cond = z3.And(z3.Not(z3.Or(*forward_conds)),
                      z3.And(*tmp_forward_conds))
        var_store, contexts = p4_state.checkpoint()
        for member_name, _ in p4_state.members:
            member_val = p4_state.resolve_reference(member_name)
            if isinstance(member_val, StructInstance):
                member_val.deactivate()
        p4_state.exit_states.append((cond, p4_state.get_z3_repr()))
        p4_state.restore(var_store, contexts)
        p4_state.has_exited = True
        context.forward_conds.append(context.tmp_forward_cond)
Ejemplo n.º 24
0
    def eval(self, ctx):
        # FIXME: This checkpointing should not be necessary
        # Figure out what is going on
        forward_conds = []
        tmp_forward_conds = []
        sub_ctx = ctx
        while not isinstance(sub_ctx, StaticContext):
            forward_conds.extend(sub_ctx.forward_conds)
            tmp_forward_conds.append(sub_ctx.tmp_forward_cond)
            sub_ctx = sub_ctx.parent_ctx

        cond = z3.And(z3.Not(z3.Or(*forward_conds)),
                      z3.And(*tmp_forward_conds))
        var_store = ctx.checkpoint()
        for member_name, _ in ctx.get_p4_state().members:
            member_val = ctx.resolve_reference(member_name)
            if isinstance(member_val, StructInstance):
                member_val.deactivate()
            ctx.add_exit_state(cond, ctx.get_p4_state().get_members(ctx))
        ctx.restore(var_store)
        ctx.set_exited(True)
        ctx.forward_conds.append(ctx.tmp_forward_cond)
Ejemplo n.º 25
0
 def eval_cases(self, ctx, cases):
     case_exprs = []
     case_matches = []
     forward_cond_copy = ctx.tmp_forward_cond
     fall_through_matches = []
     for case_match, case_block in cases.values():
         # there is no block for the switch
         # this expressions falls through to the next switch case
         if not case_block:
             fall_through_matches.append(case_match)
             continue
         # matches the condition OR all the other fall-through switches
         case_match = z3.Or(case_match, *fall_through_matches)
         fall_through_matches.clear()
         var_store = ctx.checkpoint()
         ctx.tmp_forward_cond = z3.And(
             forward_cond_copy, case_match)
         case_block.eval(ctx)
         if not (ctx.has_returned or ctx.get_exited()):
             then_vars = ctx.get_attrs()
             case_exprs.append((case_match, then_vars))
         ctx.has_returned = False
         ctx.set_exited(False)
         ctx.restore(var_store)
         case_matches.append(case_match)
     var_store = ctx.checkpoint()
     # process the default expression
     cond = z3.Not(z3.Or(*case_matches))
     if not z3.is_false(cond):
         ctx.tmp_forward_cond = z3.And(forward_cond_copy, cond)
         self.default_case.eval(ctx)
         if ctx.has_returned or ctx.get_exited():
             ctx.restore(var_store)
         ctx.has_returned = False
         ctx.set_exited(False)
         ctx.tmp_forward_cond = forward_cond_copy
     # merge all the expressions in reverse order
     for cond, then_vars in reversed(case_exprs):
         merge_attrs(ctx, cond, then_vars)
Ejemplo n.º 26
0
 def eval(self, p4_state):
     # boolean expressions can short-circuit
     # so we save the result of the right-hand expression and merge
     lval_expr = p4_state.resolve_expr(self.lval)
     var_store, chain_copy = p4_state.checkpoint()
     context = p4_state.current_context()
     forward_cond_copy = context.tmp_forward_cond
     context.tmp_forward_cond = z3.And(forward_cond_copy, lval_expr)
     rval_expr = p4_state.resolve_expr(self.rval)
     else_vars = p4_state.get_attrs()
     p4_state.restore(var_store, chain_copy)
     context.tmp_forward_cond = forward_cond_copy
     merge_attrs(p4_state, lval_expr, else_vars)
     return self.operator(lval_expr, rval_expr)
Ejemplo n.º 27
0
    def eval_switch_table_matches(self, ctx, table):
        cases = OrderedDict()
        if table.immutable:
            # if the table is immutable we can only match on const entries
            for c_keys, (action_name, _) in table.const_entries:
                const_matches = []
                # check if the action of the entry is even present
                if action_name not in self.case_blocks:
                    continue
                # compute the match key
                # FIXME: Deal with side effects here
                # Maybe just checkpoint and restore? Ugh. So expensive...
                match_cond = table.get_const_matches(ctx, c_keys)
                action = table.actions[action_name][0]
                if action_name in cases:
                    prev_match, _ = cases[action_name]
                    match_cond = z3.Or(match_cond, prev_match)
                const_matches.append(match_cond)
                cases[action_name] = (
                    match_cond, self.case_blocks[action_name])

            # we also need to process the default action separately
            # this basically hits only if no other case matches
            _, action_name, _ = table.default_action
            match_cond = z3.Not(z3.Or(*const_matches))
            if action_name in cases:
                prev_match, _ = cases[action_name]
                match_cond = z3.Or(match_cond, prev_match)
            const_matches.append(match_cond)
            cases[action_name] = (match_cond, self.case_blocks[action_name])
        else:
            # otherwise we are dealing with a normal table
            # just insert the match entries combined with the hit expression
            add_default = None
            for case_name, case_block in self.case_blocks.items():
                match_var = table.tbl_action
                action = table.actions[case_name][0]
                match_cond = z3.And(table.locals["hit"], (action == match_var))
                cases[case_name] = (match_cond, case_block)
                # we need to check if the default is in the cases
                # this implies that the "default" case can never be executed
                if case_name == table.default_action[1]:
                    add_default = (case_name, (z3.BoolVal(True), case_block))
            if add_default and len(table.actions) == len(cases):
                cases[add_default[0]] = add_default[1]
        return cases
Ejemplo n.º 28
0
    def eval(self, p4_state):
        switches = []
        for case_val, case_name in reversed(self.cases):
            case_expr = p4_state.resolve_expr(case_val)
            select_cond = []
            if isinstance(case_expr, P4ComplexInstance):
                case_expr = case_expr.flatten()
            if isinstance(case_expr, list):
                for idx, case_match in enumerate(case_expr):
                    # default implies don't care, do not add
                    # TODO: Verify that this assumption is right...
                    if isinstance(case_match, DefaultExpression):
                        continue
                    match = self.match[idx]
                    match_expr = p4_state.resolve_expr(match)
                    cond = match_expr == case_match
                    select_cond.append(cond)
            else:
                # default implies don't care, do not add
                # TODO: Verify that this assumption is right...
                if isinstance(case_expr, DefaultExpression):
                    continue
                for match in self.match:
                    match_expr = p4_state.resolve_expr(match)
                    cond = case_expr == match_expr
                    select_cond.append(cond)
            if not select_cond:
                select_cond = [z3.BoolVal(False)]
            var_store, chain_copy = p4_state.checkpoint()
            parser_state = self.state_list[case_name]
            state_expr = parser_state.eval(p4_state)
            p4_state.restore(var_store, chain_copy)
            switches.append((z3.And(*select_cond), state_expr))

        default_parser_state = self.state_list[self.default]
        expr = default_parser_state.eval(p4_state)
        for cond, state_expr in switches:
            expr = z3.If(cond, state_expr, expr)
        return expr
Ejemplo n.º 29
0
    def eval(self, p4_state):
        cond = z3.simplify(p4_state.resolve_expr(self.cond))

        # handle side effects for function and table calls
        if cond == z3.BoolVal(False):
            return p4_state.resolve_expr(self.else_val)
        if cond == z3.BoolVal(True):
            return p4_state.resolve_expr(self.then_val)

        var_store, chain_copy = p4_state.checkpoint()
        context = p4_state.current_context()
        forward_cond_copy = context.tmp_forward_cond
        context.tmp_forward_cond = z3.And(forward_cond_copy, cond)
        then_expr = p4_state.resolve_expr(self.then_val)
        then_vars = p4_state.get_attrs()
        p4_state.restore(var_store, chain_copy)
        context.tmp_forward_cond = forward_cond_copy

        else_expr = p4_state.resolve_expr(self.else_val)
        merge_attrs(p4_state, cond, then_vars)

        return handle_mux(cond, then_expr, else_expr)
Ejemplo n.º 30
0
    def eval(self, ctx):

        if self.expr is None:
            expr = None
        else:
            # resolve the expr before restoring the state
            expr = ctx.resolve_expr(self.expr)
            if isinstance(ctx.return_type, z3.BitVecSortRef):
                expr = z3_cast(expr, ctx.return_type)
            # we return a complex typed expression list, instantiate
            if isinstance(expr, list):
                # name is meaningless here so keep it empty
                instance = ctx.gen_instance("", ctx.return_type)
                instance.set_list(expr)
                expr = instance

        cond = z3.simplify(z3.And(z3.Not(z3.Or(*ctx.forward_conds)),
                                  ctx.tmp_forward_cond))
        if not z3.is_false(cond):
            ctx.return_states.append((cond, ctx.copy_attrs()))
            if expr is not None:
                ctx.return_exprs.append((cond, expr))
            ctx.has_returned = True
        ctx.forward_conds.append(ctx.tmp_forward_cond)