submodule infix-if-bridge {
  yang-version 1.1;
  belongs-to infix-interfaces {
    prefix infix-if;
  }

  import iana-if-type {
    prefix ianaift;
  }
  import ietf-interfaces {
    prefix if;
  }
  import ieee802-dot1q-types {
    prefix dot1q-types;
  }

  organization "KernelKit";
  contact      "kernelkit@googlegroups.com";
  description  "Linux bridge extension for ietf-interfaces.";

  revision 2024-02-19 {
    description "Add STP state to bridge port.";
    reference "internal";
  }
  revision 2023-12-02 {
    description "Extend bridge-port must expression to ensure a
                 bridge cannot be a bridge-port to itself.";
    reference "internal";
  }
  revision 2023-11-08 {
    description "Dropped support for configuring bridge pvid.
                 Bridge ports need explicit VLAN assignment.";
    reference "internal";
  }
  revision 2023-08-21 {
    description "Minor, lint ordering and add missing description.";
    reference "internal";
  }
  revision 2023-05-31 {
    description "Initial revision.";
    reference "internal";
  }

  /*
   * Features
   */

  feature vlan-filtering {
    description "Indicates if this bridge supports VLAN filtering.";
  }

  /*
   * Typedefs
   */
  typedef ieee-reserved-groups {
    type union {
      type uint8 {
        range "0..15";
      }
      type enumeration {
        enum stp {
          value 0;
          description "Spanning Tree (STP/RSPT/MSTP).";
        }
        enum lacp {
          value 2;
          description "802.3 Slow Protocols, e.g., LACP.";
        }
        enum dot1x {
          value 3;
          description "802.1X Port-Based Network Access Control.";
        }
        enum lldp {
          value 14;
          description "802.1AB Link Layer Discovery Protocol (LLDP).";
        }
      }
    }
    description
      "This is a user-friendly enumeration of the different reserved IEEE
       reserved link-local multicast groups, in 01:80:C2:00:00:0X.";
  }

  typedef stp-state {
    type enumeration {
      enum disabled {
        value 0;
        description "Port is in STP DISABLED state";
      }
      enum listening {
        value 1;
        description "Port is in STP LISTENING state";
      }
      enum learning {
        value 2;
        description "Port is in STP LEARNING state";
      }
      enum forwarding {
        value 3;
        description "Port is in STP FORWARDING state. This is the default vlan state.";
      }
      enum blocking {
        value 4;
        description "Port is in STP BLOCKING state.";
      }
    }
    description
      "User-friendly enumeration of different bridge port operational states.";
  }

  /*
   * Data Nodes
   */

  augment "/if:interfaces/if:interface" {
    when "derived-from-or-self(if:type,'ianaift:bridge')" {
      description "Only shown for if:type bridge";
    }

    description "Augment generic interfaces with a basic 802.1Q bridge.";

    container bridge {
      description "IEEE 802.1Q style bridge.";

      leaf-list ieee-group-forward {
        type ieee-reserved-groups;
        description
          "List of IEEE link-local protocols to forward, e.g., STP, LLDP";
      }

      container vlans {
        if-feature "vlan-filtering";
        description "A VLAN filtering bridge has at least one VLAN.";

        leaf proto {
          type dot1q-types:dot1q-tag-type;
          default dot1q-types:c-vlan;
          description "Standard (1Q/c-vlan) or provider (1ad/s-vlan) bridge.";
        }

        list vlan {
          key "vid";
          description "List of VLANs associated with the Bridge.";

          leaf vid {
            type dot1q-types:vlanid;
            description "The VLAN identifier to which this entry applies.";
          }

          leaf-list untagged {
            type if:interface-ref;
            description "The set of ports in the untagged set for VLAN.";
          }

          leaf-list tagged {
            type if:interface-ref;
            must "not(../untagged[contains(., current())])" {
              error-message "Cannot be an untagged port at the same time.";
            }
            description "The set of ports in the tagged set for VLAN.";
          }
        }
      }
    }
  }

  augment "/if:interfaces/if:interface/infix-if:port" {
    when "derived-from-or-self(if:type,'ianaift:bridge') or "+
         "derived-from-or-self(if:type,'ianaift:ethernetCsmacd') or "+
         "derived-from-or-self(if:type,'ianaift:ieee8023adLag') or "+
         "derived-from-or-self(if:type,'ianaift:ilan')" {
      description "Applies when a Bridge interface exists.";
    }

    description "Augments the interface model with the Bridge Port";

    case bridge-port {
      description "Extension of the IETF Interfaces model (RFC7223).";

      container bridge-port {
        description "Bridge association and port specific setttngs.";

        leaf bridge {
          type if:interface-ref;
          must "deref(.)/../bridge and not(. = ../../if:name)" {
            error-message "Must refer to a bridge interface (and not itself).";
          }
          mandatory true;
          description "Bridge interface to which this interface is attached.";
        }

        leaf stp-state {
          type stp-state;
          config false;
          description "The operation state of the bridge port.";
        }

        leaf pvid {
          if-feature "vlan-filtering";
          type dot1q-types:vlanid;
          default "1";
          description "The primary VID assigned to this bridge port.";
        }

        leaf default-priority {
          if-feature "vlan-filtering";
          type dot1q-types:priority-type;
          default "0";
          description "The default priority assigned to this bridge port.";
        }
      }
    }
  }
}
