-
Notifications
You must be signed in to change notification settings - Fork 0
/
m-gtp4-ipv6-src-fields.go
177 lines (159 loc) · 6.25 KB
/
m-gtp4-ipv6-src-fields.go
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright 2023 Louis Royer and the NextMN-SRv6 contributors. All rights reserved.
// Use of this source code is governed by a MIT-style license that can be
// found in the LICENSE file.
// SPDX-License-Identifier: MIT
package mup
import (
"encoding/binary"
"fmt"
"net"
"net/netip"
"github.com/google/gopacket/layers"
)
//-------------------------------------------------------
// RFC 9433: 6.6 End.M.GTP4.E
// > * The IPv6 Source Address has the following format:
// >
// > 0 127
// > +----------------------+--------+--------------------------+
// > | Source UPF Prefix |IPv4 SA | any bit pattern(ignored) |
// > +----------------------+--------+--------------------------+
// > 128-a-b a b
// >
// > IPv6 SA Encoding for End.M.GTP4.E
//
//-------------------------------------------------------
// With NextMN implementation, we choose to deviate from the RFC
// because RFC's proposal doesn't allow to retrieve
// the IPv4 SA without knowing the prefix length,
// which may be different for 2 packets issued from 2 different headends.
//
// To allow the endpoint to be stateless, we need to know the prefix.
// We propose to encode it on the 7 last bits of the IPv6 SA.
//
// The other option would have be to directly put the IPv4 SA at the end of the IPv6 SA,
// but this would imply matching on /128 if the IPv4 SA is used for source routing purpose,
// and thus breaking compatibility with future new patterns.
//
// We also introcuce a new field that will carry the source UDP port to be used in the newly created GTP4 packet.
// This field is intended to help load balancing, as specified in TS 129.281 section 4.4.2.0:
// > For the GTP-U messages described below (other than the Echo Response message, see clause 4.4.2.2), the UDP Source
// > Port or the Flow Label field (see IETF RFC 6437 [37]) should be set dynamically by the sending GTP-U entity to help
// > balancing the load in the transport network
//
// Since the headend has a better view than End.M.GTP4.E on
// the origin of the flows, and can be helped by the control plane,
// it makes sense to generate the source port number on headend side,
// and to carry it during transit through SR domain.
//
// Note: even with this proposal, the remaining space (73 bits) is bigger
// than what remains for LOC+FUNC in the SID (56 bits).
//
//
// 0 127
// +----------------------+--------+----------------+--------------------------+---------------+
// | Source UPF Prefix |IPv4 SA | UDP Source Port| any bit pattern(ignored) | Prefix length |
// +----------------------+--------+----------------+--------------------------+---------------+
// 128-a-b'-c-7 a (32 bits) c (16 bits) b' 7 bits
//
// IPv6 SA Encoding for End.M.GTP4.E in NextMN
type MGTP4IPv6SrcFields struct {
prefix netip.Prefix // prefix in canonical form
ipv4 [IPV4_ADDR_SIZE_BYTE]byte
udp [UDP_PORT_SIZE_BYTE]byte
}
func NewMGTP4IPv6SrcFieldsFromFields(prefix netip.Prefix, ipv4 []byte, udp []byte) (*MGTP4IPv6SrcFields, error) {
if len(ipv4) != 4 {
return nil, fmt.Errorf("Not a IPv4 Address")
}
return &MGTP4IPv6SrcFields{
prefix: prefix.Masked(),
ipv4: [4]byte{ipv4[0], ipv4[1], ipv4[2], ipv4[3]},
udp: [2]byte{udp[0], udp[1]},
}, nil
}
func NewMGTP4IPv6SrcFields(addr []byte) (*MGTP4IPv6SrcFields, error) {
if len(addr) != IPV6_ADDR_SIZE_BYTE {
return nil, ErrNotAnIPv6Address
}
// Prefix length extraction
prefixLen := uint(IPV6_LEN_ENCODING_MASK & (addr[IPV6_LEN_ENCODING_POS_BYTE] >> IPV6_LEN_ENCODING_POS_BIT))
if prefixLen == 0 {
// even if globally routable IPv6 Prefix size cannot currently be less than 32 (per ICANN policy),
// nothing prevent the use of such prefix with ULA (fc00::/7)
// or, in the future, a prefix from a currently not yet allocated address block.
return nil, ErrWrongValue
}
if prefixLen+IPV4_ADDR_SIZE_BIT+UDP_PORT_SIZE_BIT+IPV6_LEN_ENCODING_SIZE_BIT > IPV6_ADDR_SIZE_BIT {
return nil, ErrWrongValue
}
// prefix extraction
a, ok := netip.AddrFromSlice(addr)
if !ok {
return nil, ErrNotAnIPv6Address
}
prefix := netip.PrefixFrom(a, int(prefixLen)).Masked()
// ipv4 extraction
var ipv4 [IPV4_ADDR_SIZE_BYTE]byte
if src, err := fromSlice(addr, prefixLen, IPV4_ADDR_SIZE_BYTE); err != nil {
return nil, err
} else {
copy(ipv4[:], src[:IPV4_ADDR_SIZE_BYTE])
}
// udp port extraction
var udp [UDP_PORT_SIZE_BYTE]byte
if src, err := fromSlice(addr, prefixLen+IPV4_ADDR_SIZE_BIT, UDP_PORT_SIZE_BYTE); err != nil {
return nil, err
} else {
copy(udp[:], src[:UDP_PORT_SIZE_BYTE])
}
return &MGTP4IPv6SrcFields{
prefix: prefix,
ipv4: ipv4,
udp: udp,
}, nil
}
func (e *MGTP4IPv6SrcFields) IPv4() net.IP {
return net.IPv4(e.ipv4[0], e.ipv4[1], e.ipv4[2], e.ipv4[3])
}
func (e *MGTP4IPv6SrcFields) UDPPortNumber() layers.UDPPort {
return (layers.UDPPort)(binary.BigEndian.Uint16([]byte{e.udp[0], e.udp[1]}))
}
func (a *MGTP4IPv6SrcFields) MarshalLen() int {
return IPV6_ADDR_SIZE_BYTE
}
// Marshal returns the byte sequence generated from MGTP4IPv6SrcFields.
func (a *MGTP4IPv6SrcFields) Marshal() ([]byte, error) {
b := make([]byte, a.MarshalLen())
if err := a.MarshalTo(b); err != nil {
return nil, err
}
return b, nil
}
// MarshalTo puts the byte sequence in the byte array given as b.
// warning: no caching is done, this result will be recomputed at each call
func (a *MGTP4IPv6SrcFields) MarshalTo(b []byte) error {
if len(b) < a.MarshalLen() {
return ErrTooShortToMarshal
}
// init b with prefix
prefix := a.prefix.Addr().As16()
copy(b, prefix[:])
ipv4 := netip.AddrFrom4(a.ipv4).AsSlice()
udp := []byte{a.udp[0], a.udp[1]}
bits := a.prefix.Bits()
if bits == -1 {
return fmt.Errorf("Error with prefix length")
}
// add ipv4
if err := appendToSlice(b, uint(bits), ipv4); err != nil {
return fmt.Errorf("Error during serialization of IPv4: %s", err)
}
// add upd port
if err := appendToSlice(b, uint(bits+IPV4_ADDR_SIZE_BIT), udp); err != nil {
return fmt.Errorf("Error during serialization of UDP Port: %s", err)
}
// add prefix length
b[IPV6_LEN_ENCODING_POS_BYTE] = byte(bits)
return nil
}