github.com/cilium/cilium@v1.16.2/pkg/policy/api/icmp.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package api
     5  
     6  import (
     7  	"encoding/json"
     8  	"fmt"
     9  
    10  	"k8s.io/apimachinery/pkg/util/intstr"
    11  )
    12  
    13  const (
    14  	IPv4Family = "IPv4"
    15  	IPv6Family = "IPv6"
    16  )
    17  
    18  var icmpIpv4TypeNameToCode = map[string]string{
    19  	"EchoReply":              "0",
    20  	"DestinationUnreachable": "3",
    21  	"Redirect":               "5",
    22  	"Echo":                   "8",
    23  	"EchoRequest":            "8",
    24  	"RouterAdvertisement":    "9",
    25  	"RouterSelection":        "10",
    26  	"TimeExceeded":           "11",
    27  	"ParameterProblem":       "12",
    28  	"Timestamp":              "13",
    29  	"TimestampReply":         "14",
    30  	"Photuris":               "40",
    31  	"ExtendedEchoRequest":    "42",
    32  	"ExtendedEchoReply":      "43",
    33  }
    34  
    35  var icmpIpv6TypeNameToCode = map[string]string{
    36  	"DestinationUnreachable":                 "1",
    37  	"PacketTooBig":                           "2",
    38  	"TimeExceeded":                           "3",
    39  	"ParameterProblem":                       "4",
    40  	"EchoRequest":                            "128",
    41  	"EchoReply":                              "129",
    42  	"MulticastListenerQuery":                 "130",
    43  	"MulticastListenerReport":                "131",
    44  	"MulticastListenerDone":                  "132",
    45  	"RouterSolicitation":                     "133",
    46  	"RouterAdvertisement":                    "134",
    47  	"NeighborSolicitation":                   "135",
    48  	"NeighborAdvertisement":                  "136",
    49  	"RedirectMessage":                        "137",
    50  	"RouterRenumbering":                      "138",
    51  	"ICMPNodeInformationQuery":               "139",
    52  	"ICMPNodeInformationResponse":            "140",
    53  	"InverseNeighborDiscoverySolicitation":   "141",
    54  	"InverseNeighborDiscoveryAdvertisement":  "142",
    55  	"HomeAgentAddressDiscoveryRequest":       "144",
    56  	"HomeAgentAddressDiscoveryReply":         "145",
    57  	"MobilePrefixSolicitation":               "146",
    58  	"MobilePrefixAdvertisement":              "147",
    59  	"DuplicateAddressRequestCodeSuffix":      "157",
    60  	"DuplicateAddressConfirmationCodeSuffix": "158",
    61  	"ExtendedEchoRequest":                    "160",
    62  	"ExtendedEchoReply":                      "161",
    63  }
    64  
    65  type ICMPRules []ICMPRule
    66  
    67  // ICMPRule is a list of ICMP fields.
    68  type ICMPRule struct {
    69  	// Fields is a list of ICMP fields.
    70  	//
    71  	// +kubebuilder:validation:Optional
    72  	// +kubebuilder:validation:MaxItems=40
    73  	Fields []ICMPField `json:"fields,omitempty"`
    74  }
    75  
    76  // ICMPField is a ICMP field.
    77  //
    78  // +deepequal-gen=true
    79  // +deepequal-gen:private-method=true
    80  type ICMPField struct {
    81  	// Family is a IP address version.
    82  	// Currently, we support `IPv4` and `IPv6`.
    83  	// `IPv4` is set as default.
    84  	//
    85  	// +kubebuilder:default=IPv4
    86  	// +kubebuilder:validation:Optional
    87  	// +kubebuilder:validation:Enum=IPv4;IPv6
    88  	Family string `json:"family,omitempty"`
    89  
    90  	// Type is a ICMP-type.
    91  	// It should be an 8bit code (0-255), or it's CamelCase name (for example, "EchoReply").
    92  	// Allowed ICMP types are:
    93  	//     Ipv4: EchoReply | DestinationUnreachable | Redirect | Echo | EchoRequest |
    94  	//		     RouterAdvertisement | RouterSelection | TimeExceeded | ParameterProblem |
    95  	//			 Timestamp | TimestampReply | Photuris | ExtendedEcho Request | ExtendedEcho Reply
    96  	//     Ipv6: DestinationUnreachable | PacketTooBig | TimeExceeded | ParameterProblem |
    97  	//			 EchoRequest | EchoReply | MulticastListenerQuery| MulticastListenerReport |
    98  	// 			 MulticastListenerDone | RouterSolicitation | RouterAdvertisement | NeighborSolicitation |
    99  	// 			 NeighborAdvertisement | RedirectMessage | RouterRenumbering | ICMPNodeInformationQuery |
   100  	// 			 ICMPNodeInformationResponse | InverseNeighborDiscoverySolicitation | InverseNeighborDiscoveryAdvertisement |
   101  	// 			 HomeAgentAddressDiscoveryRequest | HomeAgentAddressDiscoveryReply | MobilePrefixSolicitation |
   102  	// 			 MobilePrefixAdvertisement | DuplicateAddressRequestCodeSuffix | DuplicateAddressConfirmationCodeSuffix |
   103  	// 			 ExtendedEchoRequest | ExtendedEchoReply
   104  	//
   105  	// +deepequal-gen=false
   106  	// +kubebuilder:validation:XIntOrString
   107  	// +kubebuilder:validation:Pattern="^([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]|EchoReply|DestinationUnreachable|Redirect|Echo|RouterAdvertisement|RouterSelection|TimeExceeded|ParameterProblem|Timestamp|TimestampReply|Photuris|ExtendedEchoRequest|ExtendedEcho Reply|PacketTooBig|ParameterProblem|EchoRequest|MulticastListenerQuery|MulticastListenerReport|MulticastListenerDone|RouterSolicitation|RouterAdvertisement|NeighborSolicitation|NeighborAdvertisement|RedirectMessage|RouterRenumbering|ICMPNodeInformationQuery|ICMPNodeInformationResponse|InverseNeighborDiscoverySolicitation|InverseNeighborDiscoveryAdvertisement|HomeAgentAddressDiscoveryRequest|HomeAgentAddressDiscoveryReply|MobilePrefixSolicitation|MobilePrefixAdvertisement|DuplicateAddressRequestCodeSuffix|DuplicateAddressConfirmationCodeSuffix)$"
   108  	Type *intstr.IntOrString `json:"type"`
   109  }
   110  
   111  func (i *ICMPField) DeepEqual(o *ICMPField) bool {
   112  	if i == nil {
   113  		return o == nil
   114  	}
   115  
   116  	if i.Type.String() != o.Type.String() {
   117  		return false
   118  	}
   119  
   120  	return i.deepEqual(o)
   121  }
   122  
   123  // UnmarshalJSON unmarshals the ICMPField from the byte array and check if the Type matches with IP version.
   124  func (i *ICMPField) UnmarshalJSON(value []byte) error {
   125  	var t struct {
   126  		Family string              `json:"family,omitempty"`
   127  		Type   *intstr.IntOrString `json:"type"`
   128  	}
   129  
   130  	if err := json.Unmarshal(value, &t); err != nil {
   131  		return err
   132  	}
   133  
   134  	// If i.Type is ICMP type name, the value should be checked if it belongs to the map for the given family.
   135  	if t.Type.String() != "0" && t.Type.IntValue() == 0 {
   136  		name := t.Type.String()
   137  		var nameToCode map[string]string
   138  		switch t.Family {
   139  		case IPv6Family:
   140  			nameToCode = icmpIpv6TypeNameToCode
   141  		default:
   142  			nameToCode = icmpIpv4TypeNameToCode
   143  		}
   144  
   145  		if _, ok := nameToCode[name]; !ok {
   146  			return fmt.Errorf("ICMP type %s not found in %s", name, t.Family)
   147  		}
   148  	}
   149  
   150  	i.Family = t.Family
   151  	i.Type = t.Type
   152  
   153  	return nil
   154  }
   155  
   156  // Iterate iterates over all elements of ICMPRules.
   157  func (ir ICMPRules) Iterate(f func(pr Ports) error) error {
   158  	for i := range ir {
   159  		if err := f(&ir[i]); err != nil {
   160  			return err
   161  		}
   162  	}
   163  	return nil
   164  }
   165  
   166  // Len returns the length of the elements of ICMPRules.
   167  func (ir ICMPRules) Len() int {
   168  	return len(ir)
   169  }
   170  
   171  // GetPortProtocols generates PortProtocol slice from ICMPRule and returns it.
   172  func (ir ICMPRule) GetPortProtocols() []PortProtocol {
   173  	var pps []PortProtocol
   174  	for _, t := range ir.Fields {
   175  		pp := t.PortProtocol()
   176  		pps = append(pps, *pp)
   177  	}
   178  	return pps
   179  }
   180  
   181  // GetPortRule generates PortRule from ICMPRule and returns it.
   182  func (ir ICMPRule) GetPortRule() *PortRule {
   183  	var pps []PortProtocol
   184  	for _, t := range ir.Fields {
   185  		pp := t.PortProtocol()
   186  		pps = append(pps, *pp)
   187  	}
   188  	pr := PortRule{
   189  		Ports: pps,
   190  	}
   191  	return &pr
   192  }
   193  
   194  // PortProtocol translates ICMPType to PortProtocol.
   195  func (i ICMPField) PortProtocol() *PortProtocol {
   196  	var proto L4Proto
   197  	var nameToCode map[string]string
   198  
   199  	switch i.Family {
   200  	case IPv6Family:
   201  		proto = ProtoICMPv6
   202  		nameToCode = icmpIpv6TypeNameToCode
   203  
   204  	default:
   205  		proto = ProtoICMP
   206  		nameToCode = icmpIpv4TypeNameToCode
   207  	}
   208  
   209  	port := i.Type.String()
   210  	if name, ok := nameToCode[port]; ok {
   211  		port = name
   212  	}
   213  
   214  	pr := PortProtocol{
   215  		Port:     port,
   216  		Protocol: proto,
   217  	}
   218  	return &pr
   219  }