github.com/sagernet/netlink@v0.0.0-20240612041022-b9a21c07ac6a/nl/seg6_linux.go (about)

     1  package nl
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  )
     8  
     9  type IPv6SrHdr struct {
    10  	nextHdr      uint8
    11  	hdrLen       uint8
    12  	routingType  uint8
    13  	segmentsLeft uint8
    14  	firstSegment uint8
    15  	flags        uint8
    16  	reserved     uint16
    17  
    18  	Segments []net.IP
    19  }
    20  
    21  func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool {
    22  	if len(s1.Segments) != len(s2.Segments) {
    23  		return false
    24  	}
    25  	for i := range s1.Segments {
    26  		if !s1.Segments[i].Equal(s2.Segments[i]) {
    27  			return false
    28  		}
    29  	}
    30  	return s1.nextHdr == s2.nextHdr &&
    31  		s1.hdrLen == s2.hdrLen &&
    32  		s1.routingType == s2.routingType &&
    33  		s1.segmentsLeft == s2.segmentsLeft &&
    34  		s1.firstSegment == s2.firstSegment &&
    35  		s1.flags == s2.flags
    36  	// reserved doesn't need to be identical.
    37  }
    38  
    39  // seg6 encap mode
    40  const (
    41  	SEG6_IPTUN_MODE_INLINE = iota
    42  	SEG6_IPTUN_MODE_ENCAP
    43  )
    44  
    45  // number of nested RTATTR
    46  // from include/uapi/linux/seg6_iptunnel.h
    47  const (
    48  	SEG6_IPTUNNEL_UNSPEC = iota
    49  	SEG6_IPTUNNEL_SRH
    50  	__SEG6_IPTUNNEL_MAX
    51  )
    52  const (
    53  	SEG6_IPTUNNEL_MAX = __SEG6_IPTUNNEL_MAX - 1
    54  )
    55  
    56  func EncodeSEG6Encap(mode int, segments []net.IP) ([]byte, error) {
    57  	nsegs := len(segments) // nsegs: number of segments
    58  	if nsegs == 0 {
    59  		return nil, errors.New("EncodeSEG6Encap: No Segment in srh")
    60  	}
    61  	b := make([]byte, 12, 12+len(segments)*16)
    62  	native := NativeEndian()
    63  	native.PutUint32(b, uint32(mode))
    64  	b[4] = 0                      // srh.nextHdr (0 when calling netlink)
    65  	b[5] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
    66  	b[6] = IPV6_SRCRT_TYPE_4      // srh.routingType (assigned by IANA)
    67  	b[7] = uint8(nsegs - 1)       // srh.segmentsLeft
    68  	b[8] = uint8(nsegs - 1)       // srh.firstSegment
    69  	b[9] = 0                      // srh.flags (SR6_FLAG1_HMAC for srh_hmac)
    70  	// srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07
    71  	native.PutUint16(b[10:], 0) // srh.reserved
    72  	for _, netIP := range segments {
    73  		b = append(b, netIP...) // srh.Segments
    74  	}
    75  	return b, nil
    76  }
    77  
    78  func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) {
    79  	native := NativeEndian()
    80  	mode := int(native.Uint32(buf))
    81  	srh := IPv6SrHdr{
    82  		nextHdr:      buf[4],
    83  		hdrLen:       buf[5],
    84  		routingType:  buf[6],
    85  		segmentsLeft: buf[7],
    86  		firstSegment: buf[8],
    87  		flags:        buf[9],
    88  		reserved:     native.Uint16(buf[10:12]),
    89  	}
    90  	buf = buf[12:]
    91  	if len(buf)%16 != 0 {
    92  		err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)", len(buf))
    93  		return mode, nil, err
    94  	}
    95  	for len(buf) > 0 {
    96  		srh.Segments = append(srh.Segments, net.IP(buf[:16]))
    97  		buf = buf[16:]
    98  	}
    99  	return mode, srh.Segments, nil
   100  }
   101  
   102  func DecodeSEG6Srh(buf []byte) ([]net.IP, error) {
   103  	native := NativeEndian()
   104  	srh := IPv6SrHdr{
   105  		nextHdr:      buf[0],
   106  		hdrLen:       buf[1],
   107  		routingType:  buf[2],
   108  		segmentsLeft: buf[3],
   109  		firstSegment: buf[4],
   110  		flags:        buf[5],
   111  		reserved:     native.Uint16(buf[6:8]),
   112  	}
   113  	buf = buf[8:]
   114  	if len(buf)%16 != 0 {
   115  		err := fmt.Errorf("DecodeSEG6Srh: error parsing Segment List (buf len: %d)", len(buf))
   116  		return nil, err
   117  	}
   118  	for len(buf) > 0 {
   119  		srh.Segments = append(srh.Segments, net.IP(buf[:16]))
   120  		buf = buf[16:]
   121  	}
   122  	return srh.Segments, nil
   123  }
   124  func EncodeSEG6Srh(segments []net.IP) ([]byte, error) {
   125  	nsegs := len(segments) // nsegs: number of segments
   126  	if nsegs == 0 {
   127  		return nil, errors.New("EncodeSEG6Srh: No Segments")
   128  	}
   129  	b := make([]byte, 8, 8+len(segments)*16)
   130  	native := NativeEndian()
   131  	b[0] = 0                      // srh.nextHdr (0 when calling netlink)
   132  	b[1] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit)
   133  	b[2] = IPV6_SRCRT_TYPE_4      // srh.routingType (assigned by IANA)
   134  	b[3] = uint8(nsegs - 1)       // srh.segmentsLeft
   135  	b[4] = uint8(nsegs - 1)       // srh.firstSegment
   136  	b[5] = 0                      // srh.flags (SR6_FLAG1_HMAC for srh_hmac)
   137  	// srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07
   138  	native.PutUint16(b[6:], 0) // srh.reserved
   139  	for _, netIP := range segments {
   140  		b = append(b, netIP...) // srh.Segments
   141  	}
   142  	return b, nil
   143  }
   144  
   145  // Helper functions
   146  func SEG6EncapModeString(mode int) string {
   147  	switch mode {
   148  	case SEG6_IPTUN_MODE_INLINE:
   149  		return "inline"
   150  	case SEG6_IPTUN_MODE_ENCAP:
   151  		return "encap"
   152  	}
   153  	return "unknown"
   154  }