Ejemplo n.º 1
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.º 2
0
    def initialize(self, context, *args, **kwargs):
        merged_args = merge_parameters(self.params, *args, **kwargs)
        for pipe_name, pipe_arg in merged_args.items():
            if pipe_arg.p4_val is None:
                # for some reason, the argument is uninitialized.
                # FIXME: This should not happen. Why?
                continue
            log.info("Loading %s pipe...", pipe_name)
            pipe_val = context.resolve_expr(pipe_arg.p4_val)
            if isinstance(pipe_val, P4Control):
                # This boilerplate is all necessary to initialize state...
                # FIXME: Ideally, this should be handled by the control...
                context.type_contexts.append(self.type_context)
                ctrl_type = resolve_type(context, pipe_arg.p4_type)
                pipe_val = pipe_val.bind_to_ctrl_type(context, ctrl_type)
                context.type_contexts.append(ctrl_type.type_context)
                args = []
                for idx, param in enumerate(pipe_val.params):
                    ctrl_type_param_type = ctrl_type.params[idx].p4_type
                    generic_type = resolve_type(context, ctrl_type_param_type)
                    if generic_type is None:
                        param_type = resolve_type(context, param.p4_type)
                        self.type_context[ctrl_type_param_type] = param_type
                    args.append(param.name)
                # create the z3 representation of this control state
                p4_state = self.z3_reg.set_p4_state(pipe_name, pipe_val.params)
                # dp not need the types for now
                context.type_contexts.pop()
                context.type_contexts.pop()

                # initialize the call with its own params we collected
                # this is essentially the input packet
                pipe_val.apply(p4_state, *args)
                # after executing the pipeline get its z3 representation
                state = p4_state.get_z3_repr()
                # and also merge back all the exit states we collected
                for exit_cond, exit_state in reversed(p4_state.exit_states):
                    state = z3.If(exit_cond, exit_state, state)
                # all done, that is our P4 representation!
                self.pipes[pipe_name] = (state, p4_state.members, pipe_val)
            elif isinstance(pipe_val, P4Extern):
                var = z3.Const(f"{pipe_name}{pipe_val.name}", pipe_val.z3_type)
                self.pipes[pipe_name] = (var, [], pipe_val)
            elif isinstance(pipe_val, P4Package):
                # execute the package by calling its initializer
                pipe_val.initialize(context)
                # resolve all the sub_pipes
                for sub_pipe_name, sub_pipe_val in pipe_val.pipes.items():
                    sub_pipe_name = f"{pipe_name}_{sub_pipe_name}"
                    self.pipes[sub_pipe_name] = sub_pipe_val
            elif isinstance(pipe_val, z3.ExprRef):
                # for some reason simple expressions are also possible.
                self.pipes[pipe_name] = (pipe_val, [], pipe_val)
            else:
                raise RuntimeError(
                    f"Unsupported value {pipe_val}, type {type(pipe_val)}."
                    " It does not make sense as a P4 pipeline.")
        return self
Ejemplo n.º 3
0
    def eval(self, p4_state):
        cond = p4_state.resolve_expr(self.cond)
        # handle side effects for function calls
        var_store, chain_copy = p4_state.checkpoint()
        then_val = p4_state.resolve_expr(self.then_val)
        then_vars = copy_attrs(p4_state.locals)
        p4_state.restore(var_store, chain_copy)
        else_val = p4_state.resolve_expr(self.else_val)
        p4_state.merge_attrs(cond, then_vars)

        then_expr = then_val
        else_expr = else_val
        # this is a really nasty hack, do not try this at home kids
        # because we have to be able to access the sub values again
        # we have to resolve the if condition in the case of complex types
        # we do this by splitting the if statement into a list
        # lists can easily be assigned to a target structure
        if isinstance(then_expr, P4ComplexInstance):
            then_expr = then_expr.flatten()
        if isinstance(else_expr, P4ComplexInstance):
            else_expr = else_expr.flatten()
        if isinstance(then_expr, list) and isinstance(else_expr, list):
            sub_cond = []
            # handle nested complex types
            then_expr = self.unravel_datatype(then_val, then_expr)
            else_expr = self.unravel_datatype(else_val, else_expr)
            for idx, member in enumerate(then_expr):
                if_expr = z3.If(cond, member, else_expr[idx])
                sub_cond.append(if_expr)
            return sub_cond
        then_is_const = isinstance(then_expr, (z3.BitVecRef, int))
        else_is_const = isinstance(else_expr, (z3.BitVecRef, int))
        if then_is_const and else_is_const:
            # align the bitvectors to allow operations, we cast ints downwards
            if else_expr.size() > then_expr.size():
                else_expr = z3_cast(else_expr, then_expr.size())
            if else_expr.size() < then_expr.size():
                then_expr = z3_cast(then_expr, else_expr.size())
        return z3.If(cond, then_expr, else_expr)
Ejemplo n.º 4
0
 def eval_callable(self, ctx, merged_args, var_buffer):
     # execute the action expression with the new environment
     ctx.return_type = self.return_type
     self.statements.eval(ctx)
     return_expr = None
     if len(ctx.return_exprs) == 1:
         _, return_expr = ctx.return_exprs.pop()
     elif len(ctx.return_exprs) > 1:
         # the first condition is not needed since it is the default
         _, return_expr = ctx.return_exprs.pop()
         if isinstance(return_expr, StructInstance):
             while ctx.return_exprs:
                 then_cond, then_expr = ctx.return_exprs.pop()
                 return_expr = handle_mux(then_cond, then_expr, return_expr)
         else:
             while ctx.return_exprs:
                 then_cond, then_expr = ctx.return_exprs.pop()
                 return_expr = z3.If(then_cond, then_expr, return_expr)
     return return_expr
Ejemplo n.º 5
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.º 6
0
 def operator(x, y):
     no_overflow = z3.BVSubNoOverflow(x, y)
     no_underflow = z3.BVSubNoUnderflow(x, y, False)
     zero_return = 0
     return z3.If(z3.And(no_overflow, no_underflow), x - y, zero_return)
Ejemplo n.º 7
0
 def operator(x, y):
     no_overflow = z3.BVAddNoOverflow(x, y, False)
     no_underflow = z3.BVAddNoUnderflow(x, y)
     max_return = 2**x.size() - 1
     return z3.If(z3.And(no_overflow, no_underflow), x + y, max_return)