theory Semantics_Goto
imports Main Firewall_Common "Common/List_Misc" "HOL-Library.LaTeXsugar"
begin

section\<open>Big Step Semantics with Goto\<close>
text\<open>
  We extend the iptables semantics to support the goto action.
  A goto directly continues processing at the start of the called chain.
  It does not change the call stack.
  In contrast to calls, goto does not return.
  Consequently, everything behind a matching goto cannot be reached.
\<close>
text\<open>
  This theory is structured as follows.
  Fist, the goto semantics are introduced.
  Then, we show that those semantics are deterministic.
  Finally, we present two methods to remove gotos.
   The first unfolds goto.
   The second replaces gotos with calls.
  Finally, since the goto rules makes all proofs quite ugly, we never mention the goto semantics again.
  As we have shown, we can get rid of the gotos easily, thus, we stick to the nicer iptables semantics without goto.
\<close>

context
begin

  subsection\<open>Semantics\<close>
    private type_synonym 'a ruleset = "string \<rightharpoonup> 'a rule list"

    private type_synonym ('a, 'p) matcher = "'a \<Rightarrow> 'p \<Rightarrow> bool"

    qualified fun matches :: "('a, 'p) matcher \<Rightarrow> 'a match_expr \<Rightarrow> 'p \<Rightarrow> bool" where
      "matches \<gamma> (MatchAnd e1 e2) p \<longleftrightarrow> matches \<gamma> e1 p \<and> matches \<gamma> e2 p" |
      "matches \<gamma> (MatchNot me) p \<longleftrightarrow> \<not> matches \<gamma> me p" |
      "matches \<gamma> (Match e) p \<longleftrightarrow> \<gamma> e p" |
      "matches _ MatchAny _ \<longleftrightarrow> True"

    (*
    main:
      call foo
      deny-all
    foo:
      goto bar
    bar:
      [nothing]

    The call returns, even if a goto is executed in the called chains. The deny-all will be executed!

    Chain OUTPUT (policy ACCEPT 98 packets, 34936 bytes)
     pkts bytes target     prot opt in     out     source               destination
        1    84            all  --  *      *       0.0.0.0/0            127.42.0.1
        1    84 foo        all  --  *      *       0.0.0.0/0            127.42.0.1
        1    84            all  --  *      *       0.0.0.0/0            127.42.0.1

    Chain bar (1 references)
     pkts bytes target     prot opt in     out     source               destination

    Chain foo (1 references)
     pkts bytes target     prot opt in     out     source               destination
        1    84 bar        all  --  *      *       0.0.0.0/0            0.0.0.0/0           [goto]

    *)
    qualified fun no_matching_Goto :: "('a, 'p) matcher \<Rightarrow> 'p \<Rightarrow> 'a rule list \<Rightarrow> bool" where
      "no_matching_Goto _ _ [] \<longleftrightarrow> True" |
      "no_matching_Goto \<gamma> p ((Rule m (Goto _))#rs) \<longleftrightarrow> \<not> matches \<gamma> m p \<and> no_matching_Goto \<gamma> p rs" |
      "no_matching_Goto \<gamma> p (_#rs) \<longleftrightarrow> no_matching_Goto \<gamma> p rs"

    inductive iptables_goto_bigstep :: "'a ruleset \<Rightarrow> ('a, 'p) matcher \<Rightarrow> 'p \<Rightarrow> 'a rule list \<Rightarrow> state \<Rightarrow> state \<Rightarrow> bool"
      ("_,_,_\<turnstile>\<^sub>g \<langle>_, _\<rangle> \<Rightarrow> _"  [60,60,60,20,98,98] 89)
      for \<Gamma> and \<gamma> and p where
    skip:    "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[], t\<rangle> \<Rightarrow> t" |
    accept:  "matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m Accept], Undecided\<rangle> \<Rightarrow> Decision FinalAllow" |
    drop:    "matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m Drop], Undecided\<rangle> \<Rightarrow> Decision FinalDeny" |
    reject:  "matches \<gamma> m p \<Longrightarrow>  \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m Reject], Undecided\<rangle> \<Rightarrow> Decision FinalDeny" |
    log:     "matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m Log], Undecided\<rangle> \<Rightarrow> Undecided" |
    empty:   "matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m Empty], Undecided\<rangle> \<Rightarrow> Undecided" |
    nomatch: "\<not> matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m a], Undecided\<rangle> \<Rightarrow> Undecided" |
    decision: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Decision X\<rangle> \<Rightarrow> Decision X" |
    seq:      "\<lbrakk>\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>1, Undecided\<rangle> \<Rightarrow> t; \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>2, t\<rangle> \<Rightarrow> t'; no_matching_Goto \<gamma> p rs\<^sub>1\<rbrakk> \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>1@rs\<^sub>2, Undecided\<rangle> \<Rightarrow> t'" |
    call_return:  "\<lbrakk> matches \<gamma> m p; \<Gamma> chain = Some (rs\<^sub>1@[Rule m' Return]@rs\<^sub>2);
                     matches \<gamma> m' p; \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>1, Undecided\<rangle> \<Rightarrow> Undecided;
                     no_matching_Goto \<gamma> p rs\<^sub>1\<rbrakk> \<Longrightarrow>
                     \<comment> \<open>we do not support a goto in the first part if you want to return\<close>
                     \<comment> \<open>probably unhandled case:\<close>
                     \<comment> \<open>\<^verbatim>\<open>main:\<close>\<close>
                     \<comment> \<open>\<^verbatim>\<open>  call foo\<close>\<close>
                     \<comment> \<open>\<^verbatim>\<open>foo:\<close>\<close>
                     \<comment> \<open>\<^verbatim>\<open>  goto bar\<close>\<close>
                     \<comment> \<open>\<^verbatim>\<open>bar:\<close>\<close>
                     \<comment> \<open>  Return //returns to \<^verbatim>\<open>call foo\<close>\<close>
                     \<comment> \<open>But this would be a really awkward ruleset!\<close>
                   \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Call chain)], Undecided\<rangle> \<Rightarrow> Undecided" |
    call_result:  "\<lbrakk> matches \<gamma> m p; \<Gamma> chain = Some rs; \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> t \<rbrakk> \<Longrightarrow>
                   \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Call chain)], Undecided\<rangle> \<Rightarrow> t" | \<comment> \<open>goto handling here seems okay\<close>
    goto_decision:  "\<lbrakk> matches \<gamma> m p; \<Gamma> chain = Some rs; \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> Decision X \<rbrakk> \<Longrightarrow>
                   \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>(Rule m (Goto chain))#rest, Undecided\<rangle> \<Rightarrow> Decision X" |
    goto_no_decision:  "\<lbrakk> matches \<gamma> m p; \<Gamma> chain = Some rs; \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> Undecided \<rbrakk> \<Longrightarrow>
                   \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>(Rule m (Goto chain))#rest, Undecided\<rangle> \<Rightarrow> Undecided"

    text\<open>
    The semantic rules again in pretty format:
    \begin{center}
    @{thm[mode=Axiom] skip [no_vars]}\\[1ex]
    @{thm[mode=Rule] accept [no_vars]}\\[1ex]
    @{thm[mode=Rule] drop [no_vars]}\\[1ex]
    @{thm[mode=Rule] reject [no_vars]}\\[1ex]
    @{thm[mode=Rule] log [no_vars]}\\[1ex]
    @{thm[mode=Rule] empty [no_vars]}\\[1ex]
    @{thm[mode=Rule] nomatch [no_vars]}\\[1ex]
    @{thm[mode=Rule] decision [no_vars]}\\[1ex]
    @{thm[mode=Rule] seq [no_vars]} \\[1ex]
    @{thm[mode=Rule] call_return [no_vars]}\\[1ex]
    @{thm[mode=Rule] call_result [no_vars]}\\[1ex]
    @{thm[mode=Rule] goto_decision [no_vars]}\\[1ex]
    @{thm[mode=Rule] goto_no_decision [no_vars]}
    \end{center}
\<close>

    private lemma deny:
      "matches \<gamma> m p \<Longrightarrow> a = Drop \<or> a = Reject \<Longrightarrow> iptables_goto_bigstep \<Gamma> \<gamma> p [Rule m a] Undecided (Decision FinalDeny)"
    by (auto intro: drop reject)

    private lemma iptables_goto_bigstep_induct
      [case_names
        Skip Allow Deny Log Nomatch Decision Seq Call_return Call_result Goto_Decision Goto_no_Decision,
       induct pred: iptables_goto_bigstep]:
    "\<lbrakk> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,s\<rangle> \<Rightarrow> t;
       \<And>t. P [] t t;
       \<And>m a. matches \<gamma> m p \<Longrightarrow> a = Accept \<Longrightarrow> P [Rule m a] Undecided (Decision FinalAllow);
       \<And>m a. matches \<gamma> m p \<Longrightarrow> a = Drop \<or> a = Reject \<Longrightarrow> P [Rule m a] Undecided (Decision FinalDeny);
       \<And>m a. matches \<gamma> m p \<Longrightarrow> a = Log \<or> a = Empty \<Longrightarrow> P [Rule m a] Undecided Undecided;
       \<And>m a. \<not> matches \<gamma> m p \<Longrightarrow> P [Rule m a] Undecided Undecided;
       \<And>rs X. P rs (Decision X) (Decision X);
       \<And>rs rs\<^sub>1 rs\<^sub>2 t t'. rs = rs\<^sub>1 @ rs\<^sub>2 \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>1,Undecided\<rangle> \<Rightarrow> t \<Longrightarrow> P rs\<^sub>1 Undecided t \<Longrightarrow>
                          \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>2,t\<rangle> \<Rightarrow> t' \<Longrightarrow> P rs\<^sub>2 t t' \<Longrightarrow> no_matching_Goto \<gamma> p rs\<^sub>1 \<Longrightarrow>
                          P rs Undecided t';
       \<And>m a chain rs\<^sub>1 m' rs\<^sub>2. matches \<gamma> m p \<Longrightarrow> a = Call chain \<Longrightarrow>
                              \<Gamma> chain = Some (rs\<^sub>1 @ [Rule m' Return] @ rs\<^sub>2) \<Longrightarrow>
                              matches \<gamma> m' p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs\<^sub>1,Undecided\<rangle> \<Rightarrow> Undecided \<Longrightarrow>
                              no_matching_Goto \<gamma> p rs\<^sub>1 \<Longrightarrow>  P rs\<^sub>1 Undecided Undecided \<Longrightarrow>
                              P [Rule m a] Undecided Undecided;
       \<And>m a chain rs t. matches \<gamma> m p \<Longrightarrow> a = Call chain \<Longrightarrow> \<Gamma> chain = Some rs \<Longrightarrow>
                         \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,Undecided\<rangle> \<Rightarrow> t \<Longrightarrow> P rs Undecided t \<Longrightarrow> P [Rule m a] Undecided t;
       \<And>m a chain rs rest X. matches \<gamma> m p \<Longrightarrow> a = Goto chain \<Longrightarrow> \<Gamma> chain = Some rs \<Longrightarrow>
                              \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,Undecided\<rangle> \<Rightarrow> (Decision X) \<Longrightarrow> P rs Undecided (Decision X) \<Longrightarrow>
                              P (Rule m a#rest) Undecided (Decision X);
       \<And>m a chain rs rest. matches \<gamma> m p \<Longrightarrow> a = Goto chain \<Longrightarrow> \<Gamma> chain = Some rs \<Longrightarrow>
                           \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,Undecided\<rangle> \<Rightarrow> Undecided \<Longrightarrow> P rs Undecided Undecided \<Longrightarrow>
                           P (Rule m a#rest) Undecided Undecided\<rbrakk> \<Longrightarrow>
     P rs s t"
    by (induction rule: iptables_goto_bigstep.induct) auto

  subsubsection\<open>Forward reasoning\<close>

    private lemma decisionD: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r, s\<rangle> \<Rightarrow> t \<Longrightarrow> s = Decision X \<Longrightarrow> t = Decision X"
      by (induction rule: iptables_goto_bigstep_induct) auto

    private lemma iptables_goto_bigstep_to_undecided: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, s\<rangle> \<Rightarrow> Undecided \<Longrightarrow> s = Undecided"
      by (metis decisionD state.exhaust)

    private lemma iptables_goto_bigstep_to_decision: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Decision Y\<rangle> \<Rightarrow> Decision X \<Longrightarrow> Y = X"
      by (metis decisionD state.inject)

    private lemma skipD: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r, s\<rangle> \<Rightarrow> t \<Longrightarrow> r = [] \<Longrightarrow> s = t"
      by (induction rule: iptables_goto_bigstep.induct) auto

    private lemma gotoD: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r, s\<rangle> \<Rightarrow> t \<Longrightarrow> r = [Rule m (Goto chain)] \<Longrightarrow> s = Undecided \<Longrightarrow> matches \<gamma> m p \<Longrightarrow>
                    \<exists> rs. \<Gamma> chain = Some rs \<and> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,s\<rangle> \<Rightarrow> t"
      by (induction rule: iptables_goto_bigstep.induct) (auto dest: skipD elim: list_app_singletonE)

    private lemma not_no_matching_Goto_singleton_cases: "\<not> no_matching_Goto \<gamma> p [Rule m a] \<longleftrightarrow> (\<exists> chain. a = (Goto chain)) \<and> matches \<gamma> m p"
      by(case_tac a) (simp_all)

    private lemma no_matching_Goto_Cons: "no_matching_Goto \<gamma> p [r] \<Longrightarrow> no_matching_Goto \<gamma> p rs \<Longrightarrow> no_matching_Goto \<gamma> p (r#rs)"
      by(cases r)(rename_tac m a, case_tac a, simp_all)

    private lemma no_matching_Goto_head: "no_matching_Goto \<gamma> p (r#rs) \<Longrightarrow> no_matching_Goto \<gamma> p [r]"
      by(cases r)(rename_tac m a, case_tac a, simp_all)
    private lemma no_matching_Goto_tail: "no_matching_Goto \<gamma> p (r#rs) \<Longrightarrow> no_matching_Goto \<gamma> p rs"
      by(cases r)(rename_tac m a, case_tac a, simp_all)

    private lemma not_no_matching_Goto_cases:
      assumes "\<not> no_matching_Goto \<gamma> p rs" "rs \<noteq> []"
      shows "\<exists>rs1 m chain rs2. rs = rs1@(Rule m (Goto chain))#rs2 \<and> no_matching_Goto \<gamma> p rs1 \<and> matches \<gamma> m p"
        using assms
        proof(induction rs)
        case Nil thus ?case by simp
        next
        case (Cons r rs)
          note Cons_outer=this
          from Cons have "\<not> no_matching_Goto \<gamma> p (r # rs)" by simp
          show ?case
          proof(cases rs)
          case Nil
            obtain m a where "r = Rule m a" by (cases r) simp
            with \<open>\<not> no_matching_Goto \<gamma> p (r # rs)\<close> Nil not_no_matching_Goto_singleton_cases have "(\<exists> chain. a = (Goto chain)) \<and> matches \<gamma> m p" by metis
            from this obtain chain where "a = (Goto chain)" and "matches \<gamma> m p" by blast
            have "r # rs = [] @ Rule m (Goto chain) # []" "no_matching_Goto \<gamma> p []" "matches \<gamma> m p"
              by (simp_all add: \<open>a = Goto chain\<close> \<open>r = Rule m a\<close> Nil \<open>matches \<gamma> m p\<close>)
            thus ?thesis by blast
          next
          case(Cons r' rs')
            with Cons_outer have "r # rs =  r # r' # rs'" by simp
            show ?thesis
            proof(cases"no_matching_Goto \<gamma> p [r]")
            case True
              with \<open>\<not> no_matching_Goto \<gamma> p (r # rs)\<close> have "\<not> no_matching_Goto \<gamma> p rs" by (meson no_matching_Goto_Cons)
              have "rs \<noteq> []" using Cons by simp
              from Cons_outer(1)[OF \<open>\<not> no_matching_Goto \<gamma> p rs\<close> \<open>rs \<noteq> []\<close>]
                obtain rs1 m chain rs2 where "rs = rs1 @ Rule m (Goto chain) # rs2" "no_matching_Goto \<gamma> p rs1" "matches \<gamma> m p" by blast
              with \<open>r # rs =  r # r' # rs'\<close> \<open>no_matching_Goto \<gamma> p [r]\<close> no_matching_Goto_Cons
                  have "r # rs = r # rs1 @ Rule m (Goto chain) # rs2 \<and> no_matching_Goto \<gamma> p (r#rs1) \<and> matches \<gamma> m p" by fast
              thus ?thesis
                apply(rule_tac x="r#rs1" in exI)
                by auto
            next
            case False
              obtain m a where "r = Rule m a" by (cases r) simp
              with False not_no_matching_Goto_singleton_cases have "(\<exists> chain. a = (Goto chain)) \<and> matches \<gamma> m p" by metis
              from this obtain chain where "a = (Goto chain)" and "matches \<gamma> m p" by blast
              have "r # rs = [] @ Rule m (Goto chain) # rs" "no_matching_Goto \<gamma> p []" "matches \<gamma> m p"
                by (simp_all add: \<open>a = Goto chain\<close> \<open>r = Rule m a\<close> \<open>matches \<gamma> m p\<close>)
              thus ?thesis by blast
            qed
          qed
        qed

    private lemma seq_cons_Goto_Undecided:
      assumes "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)], Undecided\<rangle> \<Rightarrow> Undecided"
      and "\<not> matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> Undecided"
      shows "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>Rule m (Goto chain) # rs, Undecided\<rangle> \<Rightarrow> Undecided"
      proof(cases "matches \<gamma> m p")
        case True
          from True assms have "\<exists>rs. \<Gamma> chain = Some rs \<and> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> Undecided" by(auto dest: gotoD)
          with True show ?thesis using goto_no_decision by fast
      next
        case False
        with assms have " \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)] @ rs, Undecided\<rangle> \<Rightarrow> Undecided" by(auto dest: seq)
        with False show ?thesis by simp
      qed

    private lemma seq_cons_Goto_t:
      "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)], Undecided\<rangle> \<Rightarrow> t \<Longrightarrow> matches \<gamma> m p \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>Rule m (Goto chain) # rs, Undecided\<rangle> \<Rightarrow> t"
       apply(frule gotoD)
          apply(simp_all)
       apply(clarify)
       apply(cases t)
        apply(auto intro: iptables_goto_bigstep.intros)
    done

    private lemma no_matching_Goto_append: "no_matching_Goto \<gamma> p (rs1@rs2) \<longleftrightarrow> no_matching_Goto \<gamma> p rs1 \<and>  no_matching_Goto \<gamma> p rs2"
      by(induction \<gamma> p rs1 rule: no_matching_Goto.induct) (simp_all)

    private lemma no_matching_Goto_append1: "no_matching_Goto \<gamma> p (rs1@rs2) \<Longrightarrow> no_matching_Goto \<gamma> p rs1"
      using no_matching_Goto_append by fast
    private lemma no_matching_Goto_append2: "no_matching_Goto \<gamma> p (rs1@rs2) \<Longrightarrow> no_matching_Goto \<gamma> p rs2"
      using no_matching_Goto_append by fast

    private lemma seq_cons:
      assumes "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[r],Undecided\<rangle> \<Rightarrow> t" and "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs,t\<rangle> \<Rightarrow> t'" and "no_matching_Goto \<gamma> p [r]"
      shows "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r#rs, Undecided\<rangle> \<Rightarrow> t'"
      proof -
        from assms have "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[r] @ rs, Undecided\<rangle> \<Rightarrow> t'" by (rule seq)
        thus ?thesis by simp
      qed

    context
      notes skipD[dest] list_app_singletonE[elim]
    begin
      lemma acceptD: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r, s\<rangle> \<Rightarrow> t \<Longrightarrow> r = [Rule m Accept] \<Longrightarrow> matches \<gamma> m p \<Longrightarrow> s = Undecided \<Longrightarrow> t = Decision FinalAllow"
      by (induction rule: iptables_goto_bigstep.induct) auto

  qualified fun rewrite_Goto_chain_safe :: "(string \<rightharpoonup> 'a rule list) \<Rightarrow> 'a rule list \<Rightarrow> ('a rule list) option" where
    "rewrite_Goto_chain_safe _ [] = Some []" |
    "rewrite_Goto_chain_safe \<Gamma> ((Rule m (Goto chain))#rs) =
      (case (\<Gamma> chain) of None     \<Rightarrow> None
                      |  Some rs' \<Rightarrow> (if
                                         \<not> terminal_chain rs'
                                      then
                                         None
                                      else
                                         map_option (\<lambda>rs. Rule m (Call chain) # rs) (rewrite_Goto_chain_safe \<Gamma> rs)
                                     )
      )" |
    "rewrite_Goto_chain_safe \<Gamma> (r#rs) = map_option (\<lambda>rs. r # rs) (rewrite_Goto_chain_safe \<Gamma> rs)"

  private fun rewrite_Goto_safe_internal
    :: "(string \<times> 'a rule list) list \<Rightarrow> (string \<times> 'a rule list) list \<Rightarrow> (string \<times> 'a rule list) list option" where
    "rewrite_Goto_safe_internal _ [] = Some []" |
    "rewrite_Goto_safe_internal \<Gamma> ((chain_name, rs)#cs) =
                (case rewrite_Goto_chain_safe (map_of \<Gamma>) rs of
                         None \<Rightarrow> None
                       | Some rs' \<Rightarrow> map_option (\<lambda>rst. (chain_name, rs')#rst) (rewrite_Goto_safe_internal \<Gamma> cs)
                )"

  qualified fun rewrite_Goto_safe :: "(string \<times> 'a rule list) list \<Rightarrow> (string \<times> 'a rule list) list option" where
    "rewrite_Goto_safe cs = rewrite_Goto_safe_internal cs cs"

  (*use rewrite_Goto_chain_safe whenever possible!*)
  qualified definition rewrite_Goto :: "(string \<times> 'a rule list) list \<Rightarrow> (string \<times> 'a rule list) list" where
    "rewrite_Goto cs = the (rewrite_Goto_safe cs)"

  private lemma step_IH_cong: "(\<And>s. \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs1, s\<rangle> \<Rightarrow> t = \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs2, s\<rangle> \<Rightarrow> t) \<Longrightarrow>
         \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r#rs1, s\<rangle> \<Rightarrow> t = \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>r#rs2, s\<rangle> \<Rightarrow> t"
  apply(rule iffI)
   apply(erule seqE_cons)
    apply(rule seq'_cons)
      apply simp_all
   apply(drule not_no_matching_Goto_cases)
    apply(simp; fail)
   apply(elim exE conjE, rename_tac rs1a m chain rs2a)
   apply(subgoal_tac "r = Rule m (Goto chain)")
    prefer 2
    subgoal by (simp add: Cons_eq_append_conv)
   apply(thin_tac "[r] = _ @ Rule m (Goto chain) # _")
   apply simp
   apply (metis decision decisionD seq_cons_Goto_t state.exhaust)
  apply(erule seqE_cons)
   apply(rule seq'_cons)
     apply simp_all
  apply(drule not_no_matching_Goto_cases)
   apply(simp; fail)
  apply(elim exE conjE, rename_tac rs1a m chain rs2a)
  apply(subgoal_tac "r = Rule m (Goto chain)")
   prefer 2
   subgoal by (simp add: Cons_eq_append_conv)
  apply(thin_tac "[r] = _ @ Rule m (Goto chain) # _")
  apply simp
  apply (metis decision decisionD seq_cons_Goto_t state.exhaust)
  done

  private lemma terminal_chain_decision:
    "terminal_chain rs \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, Undecided\<rangle> \<Rightarrow> t \<Longrightarrow> \<exists>X. t = Decision X"
    apply(induction rs arbitrary: t rule: terminal_chain.induct)
                                         apply simp_all
                                    apply(auto dest: iptables_goto_bigstepD)[3]
                                 apply(erule seqE_cons, simp_all, blast dest: iptables_goto_bigstepD)+ (*6s*)
    done

  private lemma terminal_chain_Goto_decision: "\<Gamma> chain = Some rs \<Longrightarrow> terminal_chain rs \<Longrightarrow> matches \<gamma> m p \<Longrightarrow>
       \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)], s\<rangle> \<Rightarrow> t \<Longrightarrow> \<exists>X. t = Decision X"
    apply(cases s)
     apply(drule gotoD, simp_all)
     apply(elim exE conjE, simp_all)
     using terminal_chain_decision apply fast
    by (meson decisionD)

  qualified theorem rewrite_Goto_chain_safe:
    "rewrite_Goto_chain_safe \<Gamma> rs = Some rs' \<Longrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs', s\<rangle> \<Rightarrow> t \<longleftrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, s\<rangle> \<Rightarrow> t"
  proof(induction \<Gamma> rs arbitrary: rs' s rule: rewrite_Goto_chain_safe.induct)
  case 1 thus ?case by (simp split: option.split_asm if_split_asm)
  next
  case (2 \<Gamma> m chain rs)
    from 2(2) obtain z x2 where "\<Gamma> chain = Some x2" and "terminal_chain x2"
            and "rs' = Rule m (Call chain) # z"
            and "Some z = rewrite_Goto_chain_safe \<Gamma> rs"
    by(auto split: option.split_asm if_split_asm)
    from 2(1) \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close> \<open>Some z = rewrite_Goto_chain_safe \<Gamma> rs\<close>
      have IH: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>z, s\<rangle> \<Rightarrow> t = \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, s\<rangle> \<Rightarrow> t" for s by simp

    have "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>Rule m (Call chain) # z, Undecided\<rangle> \<Rightarrow> t \<longleftrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>Rule m (Goto chain) # rs, Undecided\<rangle> \<Rightarrow> t"
          (is "?lhs \<longleftrightarrow> ?rhs")
    proof(intro iffI)
      assume ?lhs
      with IH obtain ti where ti1: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Call chain)], Undecided\<rangle> \<Rightarrow> ti" and ti2: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>rs, ti\<rangle> \<Rightarrow> t"
        by(auto elim: seqE_cons)
      show ?rhs
      proof(cases "matches \<gamma> m p")
      case False
        from replace_Goto_with_Call_in_terminal_chain \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close>
        have " \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Call chain)], Undecided\<rangle> \<Rightarrow> ti \<longleftrightarrow> \<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)], Undecided\<rangle> \<Rightarrow> ti"
          by fast
        with False ti1 ti2 show ?thesis by(rule_tac t=ti in seq'_cons) simp+
      next
      case True
        from ti1 \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close>
        have g: "\<Gamma>,\<gamma>,p\<turnstile>\<^sub>g \<langle>[Rule m (Goto chain)], Undecided\<rangle> \<Rightarrow> ti"
          by(subst(asm) replace_Goto_with_Call_in_terminal_chain[symmetric]) simp+
        with True \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close> obtain X where X: "ti = Decision X"
          by(blast dest: terminal_chain_Goto_decision)
        with this ti2 have "t = Decision X"
          by(simp add: decisionD)
        with g X True ti2 \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close> show ?thesis
          apply(simp)
          apply(rule seq_cons_Goto_t, simp_all)
          done
      qed
    next
      assume ?rhs
      with IH \<open>\<Gamma> chain = Some x2\<close> \<open>terminal_chain x2\<close> \<open>Some z = rewrite_Goto_chain_safe \<Gamma> rs\<close> show ?lhs
        apply -
        apply(erule seqE_cons)
         subgoal for ti
         apply simp_all
         apply(rule_tac t=ti in seq'_cons)
           apply simp_all
         using replace_Goto_with_Call_in_terminal_chain by fast
        apply simp
        apply(frule(3) terminal_chain_Goto_decision)
        apply(subst(asm) replace_Goto_with_Call_in_terminal_chain, simp_all)
        apply(rule seq'_cons, simp_all)
        apply(elim exE)
        by (simp add: decision)
    qed
    with \<open>rs' = Rule m (Call chain) # z\<close> show ?case
      apply -
      apply(rule just_show_all_bigstep_semantics_equalities_with_start_Undecided)
      by simp

  qed(auto cong: step_IH_cong)

  text\<open>Example: The semantics are actually defined (for this example).\<close>
  lemma defines "\<gamma> \<equiv> (\<lambda>_ _. True)" and "m \<equiv> MatchAny"
  shows "[''FORWARD'' \<mapsto> [Rule m Log, Rule m (Call ''foo''), Rule m Drop],
          ''foo'' \<mapsto> [Rule m Log, Rule m (Goto ''bar''), Rule m Reject],
          ''bar'' \<mapsto> [Rule m (Goto ''baz''), Rule m Reject],
          ''baz'' \<mapsto> [(Rule m Accept)]],
      \<gamma>,p\<turnstile>\<^sub>g\<langle>[Rule MatchAny (Call ''FORWARD'')], Undecided\<rangle> \<Rightarrow> (Decision FinalAllow)"
  apply(subgoal_tac "matches \<gamma> m p")
   prefer 2
   apply(simp add: \<gamma>_def m_def; fail)
  apply(rule call_result)
    apply(auto)
  apply(rule_tac t=Undecided in seq_cons)
    apply(auto intro: log)
  apply(rule_tac t="Decision FinalAllow" in seq_cons)
    apply(auto intro: decision)
  apply(rule call_result)
     apply(simp)+
  apply(rule_tac t=Undecided in seq_cons)
    apply(auto intro: log)
  apply(rule goto_decision)
    apply(simp)+
  apply(rule goto_decision)
    apply(simp)+
  apply(auto intro: accept)
  done

end

end