Exemple #1
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)
Exemple #2
0
            def extract_hdr(self, ctx, merged_args):
                hdr = merged_args[self.hdr_param_name].p4_val
                # apply the local and parent extern type ctxs
                for type_name, p4_type in self.extern_ctx.items():
                    ctx.add_type(type_name, ctx.resolve_type(p4_type))
                for type_name, p4_type in self.type_ctx.items():
                    ctx.add_type(type_name, ctx.resolve_type(p4_type))

                # advance the header index if a next field has been accessed
                hdr_stack = detect_hdr_stack_next(ctx, hdr)
                if hdr_stack:
                    compare = hdr_stack.locals[
                        "nextIndex"] >= hdr_stack.locals["size"]
                    if z3.simplify(compare) == z3.BoolVal(True):
                        raise ParserException("Index out of bounds!")

                # grab the hdr value
                hdr_expr = ctx.resolve_expr(hdr)

                hdr_expr.activate()
                bind_const = z3.Const(f"{self.name}_{self.hdr_param_name}",
                                      hdr_expr.z3_type)
                hdr_expr.bind(bind_const)

                # advance the stack, if it exists
                if hdr_stack:
                    hdr_stack.locals["lastIndex"] = hdr_stack.locals[
                        "nextIndex"]
                    hdr_stack.locals["nextIndex"] += 1
                self.call_counter += 1
Exemple #3
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
Exemple #4
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)
Exemple #5
0
 def set_context(self, p4_state, merged_args, ref_criteria):
     # we have to subclass because of slight different behavior
     # inout and out parameters are not undefined they are set
     # with a new free constant
     param_buffer = OrderedDict()
     for param_name, arg in merged_args.items():
         # Sometimes expressions are passed, resolve those first
         arg_expr = p4_state.resolve_expr(arg.p4_val)
         # for now use the param name, not the arg_name constructed here
         # FIXME: there are some passes that rename causing issues
         arg_name = f"{self.name}_{param_name}"
         # it can happen that we receive a list
         # infer the type, generate, and set
         if isinstance(arg_expr, list):
             # if the type is undefined, do nothing
             if isinstance(arg.p4_type, P4ComplexType):
                 arg_instance = gen_instance(arg_name, arg.p4_type)
                 arg_instance.set_list(arg_expr)
                 arg_expr = arg_instance
         if arg.is_ref in ref_criteria:
             # outs are left-values so the arg must be a string
             # infer the type value at runtime, param does not work yet
             # outs reset the input
             # In the case that the instance is a complex type make sure
             # to propagate the variable through all its members
             log.debug("Resetting %s to %s", arg_expr, param_name)
             if isinstance(arg_expr, P4ComplexInstance):
                 # assume that for inout header validity is not touched
                 if arg.is_ref == "inout":
                     arg_expr.bind_to_name(arg_name)
                 else:
                     arg_expr = arg_expr.p4z3_type.instantiate(arg_name)
                 # we do not know whether the expression is valid afterwards
                 arg_expr.propagate_validity_bit()
             else:
                 arg_expr = z3.Const(f"{param_name}", arg_expr.sort())
         log.debug("Copy-in: %s to %s", arg_expr, param_name)
         # it is possible to pass an int as value, we need to cast it
         if isinstance(arg_expr, int) and isinstance(
                 arg.p4_type, (z3.BitVecSortRef, z3.BitVecRef)):
             arg_expr = z3_cast(arg_expr, arg.p4_type)
         # buffer the value, do NOT set it yet
         param_buffer[param_name] = arg_expr
     # now we can set the arguments without influencing subsequent variables
     for param_name, param_val in param_buffer.items():
         p4_state.set_or_add_var(param_name, param_val)
Exemple #6
0
 def eval_action(self, p4_state, action_tuple):
     p4_action = action_tuple[0]
     p4_action_args = action_tuple[1]
     p4_action = p4_state.resolve_reference(p4_action)
     if not isinstance(p4_action, P4Action):
         raise TypeError(f"Expected a P4Action got {type(p4_action)}!")
     action_args = []
     p4_action_args_len = len(p4_action_args) - 1
     for idx, param in enumerate(p4_action.params):
         if idx > p4_action_args_len:
             if isinstance(param.p4_type, z3.SortRef):
                 action_args.append(
                     z3.Const(f"{self.name}{param.name}", param.p4_type))
             else:
                 action_args.append(param.p4_type)
         else:
             action_args.append(p4_action_args[idx])
     return p4_action(p4_state, *action_args)
Exemple #7
0
 def initialize(self, ctx, *args, **kwargs):
     merged_args = merge_parameters(self.params, *args, **kwargs)
     for pipe_name, pipe_arg in merged_args.items():
         log.info("Loading %s pipe...", pipe_name)
         pipe_val = ctx.resolve_expr(pipe_arg.p4_val)
         if isinstance(pipe_val, P4Control):
             # create the z3 representation of this control state
             state_ctx, p4_state = self.build_state_ctx(
                 ctx, pipe_name, pipe_arg, pipe_val)
             state_ctx.set_p4_state(p4_state)
             # initialize the call with its own params we collected
             # this is essentially the input packet
             args = []
             for param in pipe_val.params:
                 args.append(param.name)
             pipe_val.apply(state_ctx, *args)
             # after executing the pipeline get its z3 representation
             z3_function = p4_state.create_z3_representation(state_ctx)
             # all done, that is our P4 representation!
             self.pipes[pipe_name] = (z3_function, p4_state, 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, None, pipe_val)
         elif isinstance(pipe_val, P4Package):
             # execute the package by calling its initializer
             # pipe_val.initialize(ctx)
             # 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, None, 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