wmnsk / go-gtp Goto Github PK
View Code? Open in Web Editor NEWGTP (GPRS Tunneling Protocol) implemented in pure Golang.
License: MIT License
GTP (GPRS Tunneling Protocol) implemented in pure Golang.
License: MIT License
Hi,
Sorry but we are very active lately on gtp side, that's why you see an increase of questions from Magma 🙂
When there is a missing IE, the server normally responds with an error cause CauseMandatoryIEIncorrect
or CauseMandatoryIEMissing
. Right now per what I understand, the Cause
type does not implement any function to parse out from the message the OffendingIE type.
Is that correct? Or maybe I missed something?
This causes MarshalLen to return an incorrect value because MarshalLen based on Contents field and causes message parsing to fail.
Line with problem:
Line 223 in 235cc37
copy
builtin: " Copy returns the number of elements copied, which will be the minimum of len(src) and len(dst).". As I understand in our case dst == nil what means len(dst) == 0, so no bytes copied.Examples currently available(mme, sgw and pgw) are to show the full feature of the package.
Providing more simple example(like v2/client
, v2/server
) may be helpful for those who visit the repo for the first time.
In the current implementation v1.Relay
use ReadFromGTP()
and WriteToGTP()
but it's a waste to decapsulate T-PDU and encapsulate it with another TEID again. Just replace the TEID field by specifying the index would be faster.
Need to change the behavior of default handlers from (*Relay) Run()
to achieve this.
The current implementation of the IP address selection does not support the PDNType == 3. It always leads to the malformed IE error:
https://github.com/wmnsk/go-gtp/blob/master/gtpv2/ie/ip-addr.go#L72-L87
Line 71 in 465ed16
IPv4v6 PAA is 22 bytes long, not 23.
Also, PAA is variable length, but not extendable. Therefore the length has to match, otherwise it's invalid.
I was trying to benchmark the current implementation of adding session and a map. The performance of the current approach of iterating over the sessions seems low. Would it be better to maintain map[teid]*Session
and map[imsi]*Session`? Let me know if what/how I am benchmarking is wrong.
func benchmarkAddSession(numExisitingSessions int, b *testing.B) {
benchConn := &v2.Conn{Sessions: []*v2.Session{}}
for i := 0; i < numExisitingSessions; i++ {
imsi := fmt.Sprintf("%015d", i)
benchConn.AddSession(v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: imsi}))
}
b.ResetTimer()
for i := 1; i <= b.N; i++ {
benchConn.AddSession(v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: "001011234567891"}))
}
}
func BenchmarkAddSessionExist0(b *testing.B) { benchmarkAddSession(0, b) }
func BenchmarkAddSessionExist100(b *testing.B) { benchmarkAddSession(1e2, b) }
func BenchmarkAddSessionExist1K(b *testing.B) { benchmarkAddSession(1e3, b) }
func BenchmarkAddSessionExist10K(b *testing.B) { benchmarkAddSession(1e4, b) }
func benchmarkAddIMSISessionMap(numExisitingSessions int, b *testing.B) {
IMSISession := make(map[string]*v2.Session)
for i := 0; i < numExisitingSessions; i++ {
imsi := fmt.Sprintf("%015d", i)
IMSISession[imsi] = v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: imsi})
}
b.ResetTimer()
for i := 1; i <= b.N; i++ {
IMSISession["001011234567891"] = v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: "001011234567891"})
}
}
func BenchmarkAddIMSISessionMapExist0(b *testing.B) { benchmarkAddIMSISessionMap(0, b) }
func BenchmarkAddIMSISessionMapExist100(b *testing.B) { benchmarkAddIMSISessionMap(1e2, b) }
func BenchmarkAddIMSISessionMapExist1K(b *testing.B) { benchmarkAddIMSISessionMap(1e3, b) }
func BenchmarkAddIMSISessionMapExist10K(b *testing.B) { benchmarkAddIMSISessionMap(1e4, b) }
func BenchmarkAddIMSISessionMapExist100K(b *testing.B) { benchmarkAddIMSISessionMap(1e5, b) }
func BenchmarkAddIMSISessionMapExist1M(b *testing.B) { benchmarkAddIMSISessionMap(1e6, b) }
func benchmarkAddIMSISessionSyncMap(numExisitingSessions int, b *testing.B) {
var IMSISession sync.Map
for i := 0; i < numExisitingSessions; i++ {
imsi := fmt.Sprintf("%015d", i)
IMSISession.Store(imsi, v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: imsi}))
}
b.ResetTimer()
for i := 1; i <= b.N; i++ {
IMSISession.Store("001011234567891", v2.NewSession(dummyAddr, &v2.Subscriber{IMSI: "001011234567891"}))
}
}
func BenchmarkAddIMSISessionSyncMapExist0(b *testing.B) { benchmarkAddIMSISessionSyncMap(0, b) }
func BenchmarkAddIMSISessionSyncMapExist100(b *testing.B) { benchmarkAddIMSISessionSyncMap(1e2, b) }
func BenchmarkAddIMSISessionSyncMapExist1K(b *testing.B) { benchmarkAddIMSISessionSyncMap(1e3, b) }
func BenchmarkAddIMSISessionSyncMapExist10K(b *testing.B) { benchmarkAddIMSISessionSyncMap(1e4, b) }
func BenchmarkAddIMSISessionSyncMapExist100K(b *testing.B) { benchmarkAddIMSISessionSyncMap(1e5, b) }
func BenchmarkAddIMSISessionSyncMapExist1M(b *testing.B) { benchmarkAddIMSISessionSyncMap(1e6, b) }
BenchmarkAddSessionExist0-88 99534 11564 ns/op 17216 B/op 19 allocs/op
BenchmarkAddSessionExist100-88 62908 18471 ns/op 19248 B/op 26 allocs/op
BenchmarkAddSessionExist1K-88 28222 42054 ns/op 33584 B/op 29 allocs/op
BenchmarkAddSessionExist10K-88 3542 401157 ns/op 403505 B/op 38 allocs/op
<too long to test>
BenchmarkAddIMSISessionMapExist0-88 84746 14651 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionMapExist100-88 81644 15163 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionMapExist1K-88 95305 12134 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionMapExist10K-88 114638 8823 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionMapExist100K-88 380025 7923 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionMapExist1M-88 388423 7936 ns/op 17208 B/op 18 allocs/op
BenchmarkAddIMSISessionSyncMapExist0-88 80818 15798 ns/op 17224 B/op 19 allocs/op
BenchmarkAddIMSISessionSyncMapExist100-88 73264 15957 ns/op 17224 B/op 19 allocs/op
BenchmarkAddIMSISessionSyncMapExist1K-88 93626 12520 ns/op 17224 B/op 19 allocs/op
BenchmarkAddIMSISessionSyncMapExist10K-88 109438 9830 ns/op 17224 B/op 19 allocs/op
BenchmarkAddIMSISessionSyncMapExist100K-88 190755 9579 ns/op 17224 B/op 19 allocs/op
BenchmarkAddIMSISessionSyncMapExist1M-88 121707 8270 ns/op 17224 B/op 19 allocs/op
The names of the package is somewhat confusing. At some point before tagging v1.0.0, I’ll change the names as follows.
v0, v1, v2 => gtpv0, gtpv1, gtpv2
messages => message
ies => ie
Did you consider implement SIP client (to reach IMS server) as built-in traffic generator ?
I have created for a test function relayToOS and checked it on OSX.
Basically idea is to have a local tunnel interface and push traffic thru it via gtp-u to a gateway.
It was created on a code that I checked out more than a month ago so it is inefficient taking into account that recently there were made some improvements yet it proves the idea.
Upon session creation:
Since #34 is merged, this project cannot be built on Windows, but it should be supported by isolating platform-specific codes.
As I think there are few people trying to run GTP nodes on Windows, I'm taking this issue as low priority at this moment.
According to GTPv2 cause should be "Non-existent" as Context is v1 specific
https://www.developingsolutions.com/reference-guides/gtp-cause-codes/
Hello. Why, then I start PGW or ENB or SGW enter this error: failed to add device gtp-(device): operation not permited
Hey @wmnsk,
It looks like google.golang.org/grpc v1.36.0
introduced a dependency on github.com/envoyproxy/[email protected]
(see link below)
That is not a stable version and it is giving us conflicts between modules, preventing us to move to go-gtp v0.7.16
Is it possible to revert this change for now?
As part of #67 restructuring we addedfunc (c *Conn) AddTEID(teid uint32, session *Session)
, but this was used only in CreateSession
, which in turn is only used to send CreateSessionRequest
.
Lines 505 to 515 in a342610
CreateSessionResponse
message handling in the examples is missing the step of invoking AddTEID
on conn
.
This can be rectified in 2 ways -
Add explict call to func (c *Conn) AddTEID(teid uint32, session *Session)
in the examples
Add session arg to func (c *Conn) NewFTEID(ifType uint8, v4, v6 string)
so that TEID is generated and stored at the same time.
Might be better to use context
to handle goroutines working background.
Hey @wmnsk
I know this is not an easy feature, but by any chance, is there any way I may be missing to print GTP messages on a readable hunan format?
Right now I am using the String method to print the GTP message, but only shows few parameters. The rest are unstructured in binary format.
Note wireshark does work, but I was looking to see them on our logs for ease. Go-Diameter
has this feature and has proven to be very useful.
Below you have an example of how we are using them right now
glog.V(2).Infof("Received Create Session Response (gtp):\n%s", csResGtp.String())
which generates something like
s8_proxy | I0403 21:21:50.306126 1 proto_conversions.go:136] Received Delete Session Response (gtp):
s8_proxy | {Flags: 0x48, Type: 37, Length: 53, TEID: 0x1, SequenceNumber: 0x4, Spare: 0, Payload: []byte{0x2, 0x0, 0x2, 0x0
, 0x10, 0x0, 0x4e, 0x0, 0x23, 0x0, 0x80, 0x80, 0x21, 0x10, 0x1, 0x1, 0x0, 0x10, 0x81, 0x6, 0x0, 0x0, 0x0, 0x0, 0x83, 0x6, 0x0, 0x0,
0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0xd, 0x0, 0x0, 0x5, 0x0, 0x0, 0x11, 0x0, 0x0, 0x10, 0x0}}
And just in case you were curious, this is Go-Diameter
human readable format
session_proxy | Credit-Control-Request (CCR)
session_proxy | {Code:272,Flags:0xc0,Version:0x1,Length:632,ApplicationId:16777238,HopByHopId:0xf7426526,EndToEndId:0xadf4d7bf}
session_proxy | Session-Id {Code:263,Flags:0x40,Length:44,VendorId:0,Value:UTF8String{string;109;655;IMSI999991234567810},Padding:2}
session_proxy | Auth-Application-Id {Code:258,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{16777238}}
session_proxy | CC-Request-Type {Code:416,Flags:0x40,Length:12,VendorId:0,Value:Enumerated{1}}
session_proxy | CC-Request-Number {Code:415,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{0}}
session_proxy | Subscription-Id {Code:443,Flags:0x40,Length:44,VendorId:0,Value:Grouped{
session_proxy | Subscription-Id-Type {Code:450,Flags:0x40,Length:12,VendorId:0,Value:Enumerated{1}},
session_proxy | Subscription-Id-Data {Code:444,Flags:0x40,Length:24,VendorId:0,Value:UTF8String{999991234567810},Padding:1},
session_proxy | }}
session_proxy | Subscription-Id {Code:443,Flags:0x40,Length:40,VendorId:0,Value:Grouped{
session_proxy | Subscription-Id-Type {Code:450,Flags:0x40,Length:12,VendorId:0,Value:Enumerated{0}},
session_proxy | Subscription-Id-Data {Code:444,Flags:0x40,Length:20,VendorId:0,Value:UTF8String{1234567810},Padding:2},
session_proxy | }}
session_proxy | IP-CAN-Type {Code:1027,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{5}}
session_proxy | RAT-Type {Code:1032,Flags:0x80,Length:16,VendorId:10415,Value:Enumerated{1004}}
session_proxy | Framed-IP-Address {Code:8,Flags:0x40,Length:12,VendorId:0,Value:IPv4{172.17.185.237}}
session_proxy | Called-Station-Id {Code:30,Flags:0x40,Length:16,VendorId:0,Value:UTF8String{internet},Padding:0}
session_proxy | Supported-Features {Code:628,Flags:0x80,Length:56,VendorId:10415,Value:Grouped{
session_proxy | Vendor-Id {Code:266,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{10415}},
session_proxy | Feature-List-ID {Code:629,Flags:0x80,Length:16,VendorId:10415,Value:Unsigned32{1}},
session_proxy | Feature-List {Code:630,Flags:0x80,Length:16,VendorId:10415,Value:Unsigned32{3}},
session_proxy | }}
session_proxy | Network-Request-Support {Code:1024,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{0}}
session_proxy | Offline {Code:1008,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{0}}
session_proxy | Online {Code:1009,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{1}}
session_proxy | Bearer-Usage {Code:1000,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{0}}
session_proxy | TGPP-Selection-Mode {Code:12,Flags:0x80,Length:16,VendorId:10415,Value:UTF8String{0},Padding:3}
session_proxy | TGPP-SGSN-Address {Code:6,Flags:0x80,Length:16,VendorId:10415,Value:IPv4{172.16.1.8}}
session_proxy | TGPP-GGSN-Address {Code:7,Flags:0x80,Length:16,VendorId:10415,Value:IPv4{172.16.1.8}}
session_proxy | AN-GW-Address {Code:1050,Flags:0x80,Length:20,VendorId:10415,Value:Address{172.16.1.8},Padding:2}
session_proxy | Access-Network-Charging-Address {Code:501,Flags:0xc0,Length:20,VendorId:10415,Value:Address{172.16.1.8},Padding:2}
session_proxy | TGPP-SGSN-MCC-MNC {Code:18,Flags:0x80,Length:20,VendorId:10415,Value:UTF8String{00101},Padding:3}
session_proxy | TGPP-User-Location-Info {Code:22,Flags:0x80,Length:28,VendorId:10415,Value:OctetString{0x8200f110000100f11000000101},Padding:3}
session_proxy | QoS-Information {Code:1016,Flags:0xc0,Length:44,VendorId:10415,Value:Grouped{
session_proxy | APN-Aggregate-Max-Bitrate-DL {Code:1040,Flags:0x80,Length:16,VendorId:10415,Value:Unsigned32{97000000}},
session_proxy | APN-Aggregate-Max-Bitrate-UL {Code:1041,Flags:0x80,Length:16,VendorId:10415,Value:Unsigned32{47000000}},
session_proxy | }}
session_proxy | Default-EPS-Bearer-QoS {Code:1049,Flags:0x80,Length:88,VendorId:10415,Value:Grouped{
session_proxy | QoS-Class-Identifier {Code:1028,Flags:0xc0,Length:16,VendorId:10415,Value:Enumerated{9}},
session_proxy | Allocation-Retention-Priority {Code:1034,Flags:0x80,Length:60,VendorId:10415,Value:Grouped{
session_proxy | Priority-Level {Code:1046,Flags:0x80,Length:16,VendorId:10415,Value:Unsigned32{9}},
session_proxy | Pre-emption-Capability {Code:1047,Flags:0x80,Length:16,VendorId:10415,Value:Enumerated{1}},
session_proxy | Pre-emption-Vulnerability {Code:1048,Flags:0x80,Length:16,VendorId:10415,Value:Enumerated{1}},
session_proxy | }}
session_proxy | }}
session_proxy |
I took create session hex stream from pcap and created a test case and started stepping through the code. I then compared it to wireshark outputs. The below seems to be divergent and might be bugs -
diff --git a/v2/ies/arp.go b/v2/ies/arp.go
index d77a326..2d732e9 100644
--- a/v2/ies/arp.go
+++ b/v2/ies/arp.go
@@ -21,7 +21,7 @@ func (i *IE) PreemptionCapability() bool {
switch i.Type {
case AllocationRetensionPriority, BearerQoS:
- return (i.Payload[0] & 0x40) == 1
+ return (i.Payload[0] & 0x40) != 1
default:
return false
}
@@ -35,7 +35,7 @@ func (i *IE) PriorityLevel() (uint8, error) {
switch i.Type {
case AllocationRetensionPriority, BearerQoS:
- return i.Payload[0] & 0x3c, nil
+ return (i.Payload[0] & 0x3c) >> 2, nil
default:
return 0, &InvalidTypeError{Type: i.Type}
}
@@ -49,7 +49,7 @@ func (i *IE) PreemptionVulnerability() bool {
switch i.Type {
case AllocationRetensionPriority, BearerQoS:
- return (i.Payload[0] & 0x01) == 1
+ return (i.Payload[0] & 0x01) != 1
default:
return false
}
diff --git a/v2/ies/bearer-qos.go b/v2/ies/bearer-qos.go
index cb80752..bbd3a7e 100644
--- a/v2/ies/bearer-qos.go
+++ b/v2/ies/bearer-qos.go
@@ -47,7 +47,7 @@ func (i *IE) MBRForUplink() (uint64, error) {
if len(i.Payload) < 7 {
return 0, io.ErrUnexpectedEOF
}
- return utils.Uint40To64(i.Payload[3:7]), nil
+ return utils.Uint40To64(i.Payload[2:7]), nil
case FlowQoS:
if len(i.Payload) < 6 {
return 0, io.ErrUnexpectedEOF
In examples/sgw handler functions for S5 is overwritten each time a subscriber establishes the Session with subscriber specific information.
For constants it’s better to have string output which may be used to log, collect metrics, etc.
Hello
When we send a message we use Session waitMessage
to wait for its specific response based on its sequence number.
The response message will be catch by handlerMessage
. However, I see that func (c *Conn) handleMessage(senderAddr net.Addr, msg message.Message) error {
also includes c.validate
. That means that in case of receiving a proper sequence number, but a bad TEID, the validation will error and waitMessage
will never receive an answer for that specific sequence. That also will cause the requestor to be forced to time out, even that a reply was sent back.
Wouldn't it be better not to do that validation there, and just provide the handler with the message that matches the sequence number. The handler could implemente then the validation and trigger an error back to the caller if needed
A config file should be possible to use that would define exact content of each message. Otherwise each message has to be compose programatically and code needs to be rebuilt.
How/where should this be handled? How to survive a restart without losing all the sessions in case of scheduled and unscheduled maintenance?
To support scheduled, I am guessing, the app can stop serving, get list of sessions, encode (using gob?) and write to file. On startup read file, decode, start serving.
To support unscheduled, the app will have to not use the session store of library and instead operate on blob store (network/disk) on every message.
What do you think?
Health check with Echo message may be helpful in some cases.
Hey @wmnsk,
Is there any plans to implement Bearer Level Traffic Flow Template (Berare TFT)
any time soon.
Thanks!
Currently the helper funcs of IEs return zero-value as a first returned value but it should return error in case of unexpected payload given.
The messages currently supported with helpers are not enough. Implement more messages(like Delete Bearer Request, Context Request, etc.) to be more useful.
Hi,
Please I will like to ask if an example for generating GTPU load test towards a UPF can be made.
I was able to get something from the following repo https://gitlab.eurecom.fr/kharade/gtp-traffic-generator but unfortunately I could not build it (I have tried severally to contact the repo owner and the OAI forum but I did not get any response).
Currently I don't have in-depth Golang skills but I think this is something that might be of value, I will appreciate if someone can assist in this regard.
Hi, I encounter some issues when running pgw of gw-tester. Pgw can not start with the pgw.yml in example. I have modified the ip address in pgw,yml to make it work. But another issue comes in the terminal of pgw when I start enb. Details are shown below:
error handling message on Conn 127.0.1.14:2123: failed to handle Create Session Request: Link not found
I am wondering if I set the ip address in pgw.yml is not correct.
Any ideas?
Thanks.
The reason for that is defer cancel()
on line 66 of examples/sgw/main.go.
'defer' will be called at the end of newSGW() function while obviously it was intended to be called at the end of program life.
As result connections will be closed as soon as newSGW() returns since 'cancel' is called on underlying context.
To prevent #68 in future we should provide convenience script to use the gw-tester code and test end-to-end connectivity in CI or by developer.
Hello, has anyone tried this tester with NextEPC?
I've tried but still doesn't work.
Hi, I always get error from MME (S11), when trying to delete bearer from remote side (SGW/PGW).
failed to validate Delete Bearer Request: got invalid TEID: 0xb73f837e
I can provide further logs.
Currently when I try to print Message output is Header + Payload in hex:
{Flags: 0x32, Type: 0x10, Length: 57, TEID: 0x00000000, SequenceNumber: 0x01c8, Payload: []byte{0x2, 0x21, 0x43, 0x65, 0x87, 0x9, 0x21, 0x43, 0xf5, 0xe, 0x8, 0x10, 0x0, 0x2, 0x34, 0x56, 0x11, 0x0, 0x2, 0x34, 0x56, 0x14, 0x5, 0x83, 0x0, 0xd, 0x3, 0x69, 0x6f, 0x74, 0x4, 0x31, 0x6e, 0x63, 0x65, 0x3, 0x63, 0x6f, 0x6d, 0x85, 0x0, 0x4, 0xc0, 0xa8, 0x1, 0x1, 0x85, 0x0, 0x4, 0xa, 0xa, 0x2, 0x2}}
This is due to the fact that only header has String() interface: func (h *Header) String() string
For debugging purposes I'd suggest to have an option to print fully parsed message. Where instead of payload I could see IMSI, TEID, etc fields in readable format.
In uli.go
we have
ECI: eci & 0xfffff,
As I understand 20 bits mask here for eNodeB Id, but since ECI includes 8bit CellId we should mask 28 bits (0xFffFFff).
At least this is how wireshark parses data.
Checking specs, will prepare commit if I find reference for this
Uname info: Linux 05911578bd58 4.18.0-193.1.2.el8_2.x86_64 #1 SMP Thu May 7 16:37:54 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@05911578bd58 libgtp5gnl]# ip netns exec ue1 gtp-link add ue1
cannot create GTP device
: Operation not supported
arch_prctl(ARCH_SET_FS, 0x7f8bb4926740) = 0
mprotect(0x7f8bb42f8000, 16384, PROT_READ) = 0
mprotect(0x7f8bb4506000, 4096, PROT_READ) = 0
mprotect(0x7f8bb470b000, 4096, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7f8bb4935000, 4096, PROT_READ) = 0
munmap(0x7f8bb4929000, 39001) = 0
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 3
socket(AF_INET, SOCK_DGRAM, IPPROTO_IP) = 4
bind(3, {sa_family=AF_INET, sin_port=htons(3386), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
bind(4, {sa_family=AF_INET, sin_port=htons(2152), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
brk(NULL) = 0xf69000
brk(0xf8a000) = 0xf8a000
brk(NULL) = 0xf8a000
socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE) = 5
bind(5, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 0
getsockname(5, {sa_family=AF_NETLINK, nl_pid=133, nl_groups=00000000}, [12]) = 0
sendto(5, {{len=80, type=0x10 /* NLMSG_??? /, flags=NLM_F_REQUEST|NLM_F_ACK|0x600, seq=1619647437, pid=0}, "\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x07\x00\x03\x00\x75\x65\x31\x00\x28\x00\x12\x80\x07\x00\x01\x00"...}, 80, 0, {sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, 12) = 80
recvmsg(5, {msg_name={sa_family=AF_NETLINK, nl_pid=0, nl_groups=00000000}, msg_namelen=12, msg_iov=[{iov_base={{len=100, type=NLMSG_ERROR, flags=0, seq=1619647437, pid=133}, {error=-EOPNOTSUPP, msg={{len=80, type=0x10 / NLMSG_??? */, flags=NLM_F_REQUEST|NLM_F_ACK|0x600, seq=1619647437, pid=0}, "\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x07\x00\x03\x00\x75\x65\x31\x00\x28\x00\x12\x80\x07\x00\x01\x00"...}}}, iov_len=4096}], msg_iovlen=1, msg_controllen=0, msg_flags=0}, 0) = 100
Should we rename the module to be go-gtp instead of the github url and appropriately change the import path? Would this make it more fork friendly? I did face an issue when trying to build my app using forked copy. I could only build with local copy and not pointing to my github using the replace keyword
Introduced in #67. This will iterate through all IFType
s and not just the ones allocated on this Conn
.
Lines 695 to 702 in 7dc3c7b
This can be solved in 3 ways -
On calling NewFTEID we check if the IFType has it own TEIDSessionMap and allocate/remove TEID on that. GetSessionByTEID
will have to include IFType.
Declare upfront which IFTypes this Conn will be allocating TEIDs for, which will share the existing TEIDSessionMap. During allocate/remove we will check IFTypes on this Conn is enabled.
Lookup and match the session pointer to current session, delete if equal.
session.teidMap.rangeWithFunc(func(k, v interface{}) bool {
teid := v.(uint32)
if s, ok := c.teidSessionMap.load(teid); ok && s == session {
c.teidSessionMap.delete(teid)
}
return true
})
Hi @wmnsk, I see that when we send any of the messages we can use session.WaitMessage(sequence, GtpTimeout)
to wait for the response for this specific message. That function works because that message is received on a specific queue for that session
However, when we send and EchoRequest, there is no session concept, so we can't use WaitMessate
function.
I see that you have solved that on func Dial
where you manually handle the echo response. But that can be done because 'Dial` function has access to the private methods on the connection.
Maybe I am missing the function, but is there any way to wait for an EchoResponse similar to waitMessage
?
Thanks
Hi there,
I'm trying to execute GW tester in containers as described in Readme and in Gist ( https://gist.github.com/wmnsk/dd27b63cefe4fc1ac8dba32b9adc8f1d ) in authors blog, but I get error on attempt to start any container:
ERROR: for mme Cannot start service mme: attaching to network failed, make sure your network options are correct and check manager logs: context deadline exceeded
I also attempted to do the same through docker compose (as proposed in cncf/cnf-testbed#342 ), but result is still the same.
Any hints what should be done? Maybe some initial setup is missing? I was searching for solution for a while - ensured ports are opened, checked node is not in drain state, etc., but still no success. Is there something should be done out of steps describe in gist or patch? docker swarm init, ... ?
I can provide current patch on demand. Doesn't look like an issue with project itself, but issue seems like the best option to ask community.
Hi, I'm trying to Marshal my CreatePDPContextRequest back to binary, but got following error for IMSI IE:
too short to serialize
First I was receiving bytes and parsing them like this:
createPDPContReq, err := gtp_messages_v1.ParseCreatePDPContextRequest(gtpPacket)
I've changed some values:
createPDPContReq.SGSNAddressForSignalling = ies_v1.NewGSNAddress(gtpcAddress) createPDPContReq.SGSNAddressForSignalling.SetLength() createPDPContReq.SGSNAddressForUserTraffic = ies_v1.NewGSNAddress(gtpuAddress) createPDPContReq.SGSNAddressForUserTraffic.SetLength() createPDPContReq.TEIDDataI = ies_v1.NewTEIDDataI(downlinkTeidProxyGGSN)
Finally I wanted to Marshal this request back to binary and send it:
createPDPContReqBytes, err := createPDPContReq.Marshal()
But, I see that in:
func (c *CreatePDPContextRequest) MarshalTo(b []byte) error {
there is line, where we are making header payload:
c.Header.Payload = make([]byte, c.MarshalLen()-c.Header.MarshalLen())
But c.MarshalLen() and c.Header.MarshalLen() return the same value, so c.Header.Payload will be equal to 0.
Could you please advice? :)
Hey @wmnsk
Checking the code where Delete Bearer Request I can see there is no difference between Linked EPS Bearer ID and EPS Bearer ID. Per what I understand per 3GPP 29.274 specs, we can have either or and both have a different IE type value.
Is this intentional? How can we differentiate between them in case it were needed?
(page 123)
https://www.etsi.org/deliver/etsi_ts/129200_129299/129274/16.05.00_60/ts_129274v160500p.pdf
Hi!
I need to go through an already established GTP tunnel. To do so, I need to create the GTP header. I was wondering if your project has any kind of facilities for it. I've been looking through it and so far I couldn't find any or a similar example.
Regards,
Víctor
Hi @wmnsk , thank you for amazing project. I need to test UPF with GTP version 1 with extension header. I was wondering if your project has any kind of facilities to do that. I tried to change message.header struct without sucess. Any help about this ? I attach an example of extension header that I tried to reproduce in go-gtp.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.