aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com/vishvananda/netlink/nl/seg6_linux.go
blob: 5774cbb15aef725040490deedf1aead894a4104b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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]) != true {
			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)\n", 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"
}