package nl import ( "errors" "fmt" "net" ) type IPv6SrHdr struct { nextHdr uint8 hdrLen uint8 routingType uint8 segmentsLeft uint8 firstSegment uint8 flags uint8 reserved uint16 Segments []net.IP } func (s1 *IPv6SrHdr) Equal(s2 IPv6SrHdr) bool { if len(s1.Segments) != len(s2.Segments) { return false } for i := range s1.Segments { if !s1.Segments[i].Equal(s2.Segments[i]) { return false } } return s1.nextHdr == s2.nextHdr && s1.hdrLen == s2.hdrLen && s1.routingType == s2.routingType && s1.segmentsLeft == s2.segmentsLeft && s1.firstSegment == s2.firstSegment && s1.flags == s2.flags // reserved doesn't need to be identical. } // seg6 encap mode const ( SEG6_IPTUN_MODE_INLINE = iota SEG6_IPTUN_MODE_ENCAP ) // number of nested RTATTR // from include/uapi/linux/seg6_iptunnel.h const ( SEG6_IPTUNNEL_UNSPEC = iota SEG6_IPTUNNEL_SRH __SEG6_IPTUNNEL_MAX ) const ( SEG6_IPTUNNEL_MAX = __SEG6_IPTUNNEL_MAX - 1 ) func EncodeSEG6Encap(mode int, segments []net.IP) ([]byte, error) { nsegs := len(segments) // nsegs: number of segments if nsegs == 0 { return nil, errors.New("EncodeSEG6Encap: No Segment in srh") } b := make([]byte, 12, 12+len(segments)*16) native := NativeEndian() native.PutUint32(b, uint32(mode)) b[4] = 0 // srh.nextHdr (0 when calling netlink) b[5] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit) b[6] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA) b[7] = uint8(nsegs - 1) // srh.segmentsLeft b[8] = uint8(nsegs - 1) // srh.firstSegment b[9] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac) // srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07 native.PutUint16(b[10:], 0) // srh.reserved for _, netIP := range segments { b = append(b, netIP...) // srh.Segments } return b, nil } func DecodeSEG6Encap(buf []byte) (int, []net.IP, error) { native := NativeEndian() mode := int(native.Uint32(buf)) srh := IPv6SrHdr{ nextHdr: buf[4], hdrLen: buf[5], routingType: buf[6], segmentsLeft: buf[7], firstSegment: buf[8], flags: buf[9], reserved: native.Uint16(buf[10:12]), } buf = buf[12:] if len(buf)%16 != 0 { err := fmt.Errorf("DecodeSEG6Encap: error parsing Segment List (buf len: %d)", len(buf)) return mode, nil, err } for len(buf) > 0 { srh.Segments = append(srh.Segments, net.IP(buf[:16])) buf = buf[16:] } return mode, srh.Segments, nil } func DecodeSEG6Srh(buf []byte) ([]net.IP, error) { native := NativeEndian() srh := IPv6SrHdr{ nextHdr: buf[0], hdrLen: buf[1], routingType: buf[2], segmentsLeft: buf[3], firstSegment: buf[4], flags: buf[5], reserved: native.Uint16(buf[6:8]), } buf = buf[8:] if len(buf)%16 != 0 { err := fmt.Errorf("DecodeSEG6Srh: error parsing Segment List (buf len: %d)", len(buf)) return nil, err } for len(buf) > 0 { srh.Segments = append(srh.Segments, net.IP(buf[:16])) buf = buf[16:] } return srh.Segments, nil } func EncodeSEG6Srh(segments []net.IP) ([]byte, error) { nsegs := len(segments) // nsegs: number of segments if nsegs == 0 { return nil, errors.New("EncodeSEG6Srh: No Segments") } b := make([]byte, 8, 8+len(segments)*16) native := NativeEndian() b[0] = 0 // srh.nextHdr (0 when calling netlink) b[1] = uint8(16 * nsegs >> 3) // srh.hdrLen (in 8-octets unit) b[2] = IPV6_SRCRT_TYPE_4 // srh.routingType (assigned by IANA) b[3] = uint8(nsegs - 1) // srh.segmentsLeft b[4] = uint8(nsegs - 1) // srh.firstSegment b[5] = 0 // srh.flags (SR6_FLAG1_HMAC for srh_hmac) // srh.reserved: Defined as "Tag" in draft-ietf-6man-segment-routing-header-07 native.PutUint16(b[6:], 0) // srh.reserved for _, netIP := range segments { b = append(b, netIP...) // srh.Segments } return b, nil } // Helper functions func SEG6EncapModeString(mode int) string { switch mode { case SEG6_IPTUN_MODE_INLINE: return "inline" case SEG6_IPTUN_MODE_ENCAP: return "encap" } return "unknown" }