Coder Social home page Coder Social logo

go-diameter's Introduction

Diameter Base Protocol

Go Report Card Test Status Go Reference Latest

Package go-diameter is an implementation of the Diameter Base Protocol RFC 6733 and a stack for the Go programming language.

Status

The current implementation is solid and works fine for general purpose clients and servers. It can send and receive messages efficiently as well as build and parse AVPs based on dictionaries.

See the API documentation at https://pkg.go.dev/github.com/fiorix/go-diameter/v4

Features

  • Comprehensive XML dictionary format
  • Embedded dictionaries:
  • Human readable AVP representation (for debugging)
  • TLS, IPv4 and IPv6 support for both clients and servers
  • Stack based on net/http for simplicity
  • Ships with sample client, server, snoop agent and benchmark tool
  • State machines for CER/CEA and DWR/DWA for clients and servers
  • TCP and SCTP support. SCTP support relies on kernel SCTP implementation and external github.com/ishidawataru/sctp package and is currently tested and enabled for Go 1.8+ and x86 Linux

Getting started

The easiest way to get started is by trying out the client and server example programs.

With Go 1.11 and newer (preferred), you can start the client and server already:

export GO111MODULE=on
go run github.com/fiorix/go-diameter/v4/examples/server
go run github.com/fiorix/go-diameter/v4/examples/client -hello

Without modules, use standard procedure:

go get github.com/fiorix/go-diameter/examples/...
go run github.com/fiorix/go-diameter/examples/server
go run github.com/fiorix/go-diameter/examples/client -hello

Source code is your best friend. Check out other examples and test cases.

Performance

Clients and servers written with the go-diameter package can be quite performant if done well. Besides Go benchmarks, the package ships with a simple benchmark tool to help testing servers and identifying bottlenecks.

In the examples directory, the server has a pprof (http server) that allows the go pprof tool to profile the server in real time. The client can perform benchmarks using the -bench command line flag.

For better performance, avoid logging diameter messages. Although logging is very useful for debugging purposes, it kills performance due to a number of conversions to make messages look pretty. If you run benchmarks on the example server, make sure to use the -s (silent) command line switch.

TLS degrades performance a bit, as well as reflection (Unmarshal). Those are important trade offs you might have to consider.

Besides this, the source code (and sub-packages) have function benchmarks that can help you understand what's fast and isn't. You will see that parsing messages is much slower than writing them, for example. This is because in order to parse messages it makes numerous dictionary lookups for AVP types, to be able to decode them. Encoding messages require less lookups and is generally simpler, thus faster.

Contribute

In case you want to add new AVPs, please add them to diam/dict/testdata xml files. Then regenerate the go models using ./autogen.sh you will find at diam folder. This will modify files at diam/dict to include your changes.

Before submitting PR, please run make test to test your changes. Or do it manually:

	go test ./...

You also have the option to run the test using a Linux VM through Docker (this is not mandatory). To do so, run make test_docker. Runing test on Linux can be useful in case you add sctp tests. Note you will need to install docker and docker-compose.

go-diameter's People

Contributors

0rac1e avatar aholappa avatar bearmini avatar danbogos avatar dwilkie avatar emakeev avatar fiorix avatar gerjimenez avatar gmd20 avatar h8liu avatar higebu avatar jaroszan avatar lwlee2608 avatar lxfontes avatar micrypt avatar mspronk avatar pmcgleenon avatar rakshasa avatar rudchenkos avatar teov avatar uri200 avatar vdvsx avatar vildam avatar yoone avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

go-diameter's Issues

Unsupported mandatory AVP found in Base package

Hello Fiorix!

I am trying to make GoDiameter to communicate with freeDiameter respectively.
However, received CEA includes three application Ids and one unknown AVP.
Despite DIAMETER_SUCCESS (2001) , freeDiameter fails to parse the response correctly.

freeDiameter Log:

18:02:54  NOTI   SND to 'cgrates-poc.opencloud.com':
18:02:54  NOTI        'Capabilities-Exchange-Request'
18:02:54  NOTI          Version: 0x01
18:02:54  NOTI          Length: 212
18:02:54  NOTI          Flags: 0x80 (R---)
18:02:54  NOTI          Command Code: 257
18:02:54  NOTI          ApplicationId: 0
18:02:54  NOTI          Hop-by-Hop Identifier: 0x1BFA6EE1
18:02:54  NOTI          End-to-End Identifier: 0x8E10F059
18:02:54  NOTI           {internal data}: src:(nil)(0) rwb:(nil) rt:0 cb:(nil),(nil)((nil)) qry:(nil) asso:0 sess:(nil)
18:02:54  NOTI           AVP: 'Origin-Host'(264) l=34 f=-M val="freediameter.opencloud.com"
18:02:54  NOTI           AVP: 'Origin-Realm'(296) l=21 f=-M val="opencloud.com"
18:02:54  NOTI           AVP: 'Origin-State-Id'(278) l=12 f=-M val=1492441313 (0x58f4d8e1)
18:02:54  NOTI           AVP: 'Host-IP-Address'(257) l=14 f=-M val=10.224.228.36
18:02:54  NOTI           AVP: 'Vendor-Id'(266) l=12 f=-M val=0 (0x0)
18:02:54  NOTI           AVP: 'Product-Name'(269) l=20 f=-- val="freeDiameter"
18:02:54  NOTI           AVP: 'Firmware-Revision'(267) l=12 f=-- val=10201 (0x27d9)
18:02:54  NOTI           AVP: 'Inband-Security-Id'(299) l=12 f=-M val='NO_INBAND_SECURITY' (0 (0x0))
18:02:54  NOTI           AVP: 'Acct-Application-Id'(259) l=12 f=-M val=3 (0x3)
18:02:54  NOTI           AVP: 'Auth-Application-Id'(258) l=12 f=-M val=4294967295 (0xffffffff)
18:02:54  NOTI           AVP: 'Supported-Vendor-Id'(265) l=12 f=-M val=5535 (0x159f)
18:02:54  NOTI           AVP: 'Supported-Vendor-Id'(265) l=12 f=-M val=10415 (0x28af)
18:02:54   DBG   'STATE_WAITCNXACK'     -> 'STATE_WAITCEA'      'cgrates-poc.opencloud.com'
18:02:54  NOTI   Unsupported mandatory AVP found
18:02:54  ERROR  Unsupported AVP: 0(not searched in dictionary) l=12 f=-M val=(not set)
18:02:54  ERROR  ERROR: in '((parsedict_do_avp(dict, ((struct avp *)(avpch->o)), mandatory, error_info)))' :    Operation not supported
18:02:54  ERROR  ERROR: in '((parsedict_do_avp(dict, ((struct avp *)(avpch->o)), mandatory, error_info)))' :    Operation not supported
18:02:54  NOTI   RCV from 'cgrates-poc.opencloud.com':
18:02:54  NOTI        'Capabilities-Exchange-Answer'
18:02:54  NOTI          Version: 0x01
18:02:54  NOTI          Length: 228
18:02:54  NOTI          Flags: 0x00 (----)
18:02:54  NOTI          Command Code: 257
18:02:54  NOTI          ApplicationId: 0
18:02:54  NOTI          Hop-by-Hop Identifier: 0x1BFA6EE1
18:02:54  NOTI          End-to-End Identifier: 0x8E10F059
18:02:54  NOTI           {internal data}: src:cgrates-poc.opencloud.com(25) rwb:0x7f8ffc0019f0 rt:0 cb:(nil),(nil)((nil)) qry:0x7f90040012f0 asso:0 sess:(nil)
18:02:54  NOTI           AVP: 'Result-Code'(268) l=12 f=-M val='DIAMETER_SUCCESS' (2001 (0x7d1))
18:02:54  NOTI           AVP: 'Origin-Host'(264) l=19 f=-M val="cgrates-poc"
18:02:54  NOTI           AVP: 'Origin-Realm'(296) l=21 f=-M val="opencloud.com"
18:02:54  NOTI           AVP: 'Host-IP-Address'(257) l=14 f=-M val=10.224.228.186
18:02:54  NOTI           AVP: 'Vendor-Id'(266) l=12 f=-M val=0 (0x0)
18:02:54  NOTI           AVP: 'Product-Name'(269) l=19 f=-- val="Golant-data"
18:02:54  NOTI           AVP: 'Origin-State-Id'(278) l=12 f=-M val=1492441313 (0x58f4d8e1)
18:02:54  NOTI           AVP: 'Acct-Application-Id'(259) l=12 f=-M val=3 (0x3)
18:02:54  NOTI           AVP: 'Auth-Application-Id'(258) l=12 f=-M val=4 (0x4)
18:02:54  NOTI           AVP: 'Auth-Application-Id'(258) l=12 f=-M val=1 (0x1)
18:02:54  NOTI           AVP: 'Supported-Vendor-Id'(265) l=12 f=-M val=10415 (0x28af)
18:02:54  NOTI           AVP: 'Vendor-Specific-Application-Id'(260) l=32 f=-M val=(grouped)
18:02:54  NOTI              AVP: 'Vendor-Id'(266) l=12 f=-M val=10415 (0x28af)
18:02:54  NOTI              AVP: 0(not searched in dictionary) l=12 f=-M val=(not set)
18:02:54  NOTI           AVP: 267(not searched in dictionary) l=12 f=-- val=(not set)
18:02:54  NOTI   Unsupported mandatory AVP found

Trace:

Diameter Protocol
    Version: 0x01
    Length: 196
    Flags: 0x00
    Command Code: 257 Capabilities-Exchange
    ApplicationId: Diameter Common Messages (0)
    Hop-by-Hop Identifier: 0x5ff41e87
    End-to-End Identifier: 0xc68a0593
    [Request In: 124]
    [Response Time: 0.000189000 seconds]
    AVP: Result-Code(268) l=12 f=-M- val=DIAMETER_SUCCESS (2001)
    AVP: Origin-Host(264) l=19 f=-M- val=cgrates-poc
    AVP: Origin-Realm(296) l=21 f=-M- val=opencloud.com
    AVP: Host-IP-Address(257) l=14 f=-M- val=10.224.228.186
    AVP: Vendor-Id(266) l=12 f=-M- val=10415
    AVP: Product-Name(269) l=19 f=--- val=Golant-data
    AVP: Origin-State-Id(278) l=12 f=-M- val=1492106344
    AVP: Acct-Application-Id(259) l=12 f=-M- val=Diameter Base Accounting (3)
    AVP: Auth-Application-Id(258) l=12 f=-M- val=Diameter Credit Control Application (4)
    AVP: Auth-Application-Id(258) l=12 f=-M- val=NASREQ Application (1)
    **AVP: Unknown(0) l=12 f=-M- val=00000004**
    AVP: Firmware-Revision(267) l=12 f=--- val=918

From the documentation, I understand that actually four dictionaries (applications) are pre-loaded in.

Embedded dictionaries:

- Base Protocol RFC 6733
- Credit Control RFC 4006
- Network Access Server RFC 7155
- 3GPP specific AVPs from TS 32.299 version 12.7.0  <=== What is that exactly and what is used for ?

I am a newbie in diameter world, but I feel, it could be related to to RFC3588 and "Vendor-Specific-Application-Id" grouped AVP.
Could you kindly point me towards the reason why that particular unknown AVP appears in the answer and which dictionary could possibly make freeDiameter understand that?

Is there a way to disable those specific AVPs from being added to CEA, since I only need DCCA?

Just tried to comment out the following in the autogen.sh file, and running it, then building the project.


// Default is a Parser object with pre-loaded
// Base Protocol and Credit Control dictionaries.
var Default *Parser

func init() {
        Default, _ = NewParser()
//      Default.Load(bytes.NewReader([]byte(baseXML)))
        Default.Load(bytes.NewReader([]byte(creditcontrolXML)))
//      Default.Load(bytes.NewReader([]byte(networkaccessserverXML)))
//      Default.Load(bytes.NewReader([]byte(tgpprorfXML)))
}

The AVP is removed, but freeDiameter rejects the session without base dictionary, so, I think it is mandatory.
This is more like a question, still not sure where is the exact issue.

Please assist,
Thanks in advance,

include all of its supported applications

on the RFC 6733 page 61, stated:
"The sender of the Capabilities-Exchange-Answer (CEA) SHOULD include all of its supported applications as a hint to the receiver regarding all of its application capabilities."

but in the implementation on /go-diameter/diam/sm/cer.go:

    if cer.AcctApplicationID != nil {
        for _, acct := range cer.AcctApplicationID {
            a.AddAVP(acct)
        }
    }
    if cer.AuthApplicationID != nil {
        for _, auth := range cer.AuthApplicationID {
            a.AddAVP(auth)
        }
    }
    if cer.VendorSpecificApplicationID != nil {
        for _, vs := range cer.VendorSpecificApplicationID {
            a.AddAVP(vs)
        }
    }

CCR - Event-Timestamp

For some reason I am having trouble pulling this value out using the avp struct tags, I can clearly see it in my payload and I can see that it is being correctly decoded in util.go, But I just get an empty time back into my struct.

However it works if I do a FindAVP(avp.EventTimestamp, 0)

I am sure I am just missing something..but I am lost as to what I am messing up. any suggestions?

Package install failure: import cycle error (Go 1.5.3)

Attempting to install the package currently fails with an import cycle error.

micrypt@box /usr/local/bin [master] $  echo $GOPATH
/Users/micrypt/silo/gocode
micrypt@box /usr/local/bin [master] $  echo $GOROOT
/Users/micrypt/silo/go
micrypt@box /usr/local/bin [master] $  go get github.com/fiorix/go-diameter/diam
import cycle not allowed
package github.com/fiorix/go-diameter/diam
    imports bufio
    imports bytes
    imports errors
    imports runtime
    imports runtime/internal/atomic
    imports runtime

Builtin handling of requests with zero value Hop-By-Hop or End-to-End header fields

When using go-diameter examples, or sm handleCER:
Upon receiving a request with hop-by-hop or end-to-end id set to zero, go-diameter will respond with a message containing random hop-by-hop / end-to-end id. The client will then discard the answer.

To the best of my understanding: While RFC 3588 or 6733 encourage the use of unique Hop-by-Hop or End-to-End field values, they do not explicitly forbid zero values. The RFCs do require that Diameter agent respond to a message with hop-by-hop and end-to-end ids equal to those received in the request.

The issue is with lack of distinction method argument zero values, and header zeros copied from request message header fields.
from NewMessage:

        if hopbyhop == 0 {
		hopbyhop = rand.Uint32()
	}
	if endtoend == 0 {
		endtoend = rand.Uint32()
	}

Example of an ugly fix for sm/cer.go successCER:

        a := m.Answer(diam.Success)
	a.Header.HopByHopID = m.Header.HopByHopID
	a.Header.EndToEndID = m.Header.EndToEndID

error on go get

go-diameter/diam/network_sctp.go:389:16: l.AcceptSCTP undefined (type sctpListener has no field or method AcceptSCTP)

Error "missing Origin-State-Id" whilst using diam/sm.

Attempting to use state machine in a similar fashion as the client.go example, results in an error, missing Origin-State-Id. There's no clear way to update the handshake CER with the AVP in question in the case that it is indeed missing. A snippet from the application is below:

    smcli := &sm.Client{
        Dict:               dict.Default,
        Handler:            mux,
        MaxRetransmits:     3,
        RetransmitInterval: time.Second,
        EnableWatchdog:     true,
        WatchdogInterval:   5 * time.Second,
        AcctApplicationID: []*diam.AVP{diam.NewAVP(avp.AcctApplicationID, avp.Mbit, 0,
            AcctApplicationID),
        },
    }

    // Set message handlers.
    mux.HandleFunc("CCA", cl.ccaHandler)

    // Print error reports.
    go printErrors(mux.ErrorReports())

    connect := func() (diam.Conn, error) {
        c, err := smcli.Dial(serverAddress)
        cl.c = c
        return cl.c, err
    }

Extracting grouped AVP from incoming message

In case of grouped/nested AVPs in the incoming message it is not clear how to properly extract values assigned to those AVPs, for example AVPs defined as follows:

r.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{
                    AVP: []*diam.AVP{
                            diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0x00)),
                            diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String(msisdn)),
                    },
            })

or

r.NewAVP(avp.MultipleServicesCreditControl, avp.Mbit, 0, &diam.GroupedAVP{
                    AVP: []*diam.AVP{
                            diam.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
                                    AVP: []*diam.AVP{
                                            diam.NewAVP(avp.CCTime, avp.Mbit, 0, datatype.Unsigned32(120)),
                                    },
                            }),
                    },
            })

FindAVP seems to work only for the first level of nesting. What would be the recommended way of extracting CCTime for the example above?

Handling AVPs where the payload is empty

Currently if AVP doesn't contain any payload it causes go-diameter to panic when parsing the message. The problem happens in unsigned32.go:17, and likewise for other datatypes. Example case where I encountered this is Failed-AVP containing AVP that has no payload.

RFC says:

A Diameter message SHOULD contain one Failed-AVP AVP, containing the
entire AVP that could not be processed successfully. If the failure
reason is omission of a required AVP, an AVP with the missing AVP
code, the missing Vendor-Id, and a zero-filled payload of the minimum
required length for the omitted AVP will be added.

So it seems it is recommended to zero fill the payload but it's not mandatory.

Multiple diameter payloads

I am just wondering if there is any helpers do deal with multiple diameter payloads within a single TCP packet?

Currently i have some that contain up to 4 distinct diameter messages per payload

sorry if i have missed something obvious

Requests that resulted in permanent errors should not be attempted again

RFC6733, 7.1.5:

Errors that fall within the permanent failures category are used to
inform the peer that the request failed and should not be attempted
again.

Currently go-diameter sm client attempts to reconnect even if permanent error code (eg. 5010) is received. Steps that take place are:

  1. TCP connection is established
  2. CE is performed, remote peer returns 5010
  3. TCP connection is closed
  4. If limit for retransmits is not exceeded go to step 1.

Tool for converting go-diameter dictionaries to wireshark dictionaries?

I just noticed there is a tool that converts wireshark dictionaries to go-diameter dictionaries. I guess there is no tool that does the conversion other way? Of course to prevent this problem one could always just create the wireshark dictionary first, but sometimes to realize this it can be already too late :)

It seems that it lacks of peer management and routing?

According to the RFC 6733, at least below features are missing in this implementation?

  • connection election between peers
  • peer tables and management
  • routing table and message routing

I think the practical diameter application should need these features.

OctetString should be initialized from string of octets, not as text.

It seems that there is an issue with OctetString datatype, according to ASN.1 OCTET STRING is a string of octets and should be initialized as a string of octets, not from quoted text, ie. an argument to datatype.OctetString should be passed to network as it is, without further conversion.

I am using this source as a reference - http://luca.ntop.org/Teaching/Appunti/asn1.html , and from what I see the practical application confirms this.

Problem having vendor specific AVP with an existing AVP code

According to RFC:
"The AVP Code, combined with the Vendor-Id field, identifies the attribute uniquely."

But currently I seem to have some parsing problems if you use a vendor specific AVP with a code that already exists. At least in avp/codes.go this looks a bit odd:

dictAVP, err := dictionary.FindAVP(application, a.Code)

Because the FindAVP doesn't take vendorId as parameter.

[Question] - Most performant decode

I am just wondering what the best performing decode method is? I am currently using the library to decode from raw bytes that are being collected from a network tap

Generally it works perfectly but occasionally if there is a message storm or some other network issues we can see a huge spike in diameter messages (hundreds of thousands per second), which can cause some issues

I am currently using ReadMessage on each packet, Just wondering if there is a better method

Thanks

Updating AVP values

It would be useful to add a method for updating AVP values. Currently I have been using this helper function:

func ReplaceAVPValue(m *diam.Message, avpName string, avpValue datatype.Type) error {

avp, err := m.FindAVP(avpName)

if err != nil {
    return err
}

oldLength := avp.Len()
avp.Data = avpValue
newLength := avp.Len()
m.Header.MessageLength += uint32(newLength) - uint32(oldLength)

return nil

}

But I'm not sure if it works for AVPs that are inside grouped AVP.

examples/server and examples/client don't connect

I tried to run examples/server/server.go and make a test-request using examples/client/client.go.

the server seems to come up:

dirk:~/gopath/src/github.com/fiorix/go-diameter/examples/server$ go run server.go
2016/10/01 09:28:28 Starting diameter server on :3868

In another shell I open the client, but I am unable to connect

dirk:~/gopath/src/github.com/fiorix/go-diameter/examples/client$ go run client.go 
2016/10/01 09:40:50 Use wireshark to see the messages, or try -hello
2016/10/01 09:40:50 no common application
2016/10/01 09:40:52 no common application

Is this expected? I must admit that these are my first steps in go.

The server responds with an error to a simple telnet localhost 3868 as expected. I am using go1.6 on ubuntu 16.4

Support Multiple Dictionaries

func NewMessage in message.go accepts a dictionary argument as the final argument:

func NewMessage(cmd uint32, flags uint8, appid, hopbyhop, endtoend uint32, dictionary *dict.Parser) *Message {

however some of the AVP's are shared and are used across multiple dictionaries

For example:

<!-- dict/testdata/base.xml -->
<avp name="Session-Id" code="263" must="M" may="P" must-not="V" may-encrypt="Y">
  <data type="UTF8String"/>
</avp>
<!-- dict/testdata/credit_control.xml -->
<command code="272" short="CC" name="Credit-Control">
  <request>
    <rule avp="Session-Id" required="true" max="1"/>
    <rule avp="Origin-Host" required="true" max="1"/>
    <!-- More AVP Rules >

I'm not sure what the best approach here is, but maybe we can pass in an array of dictionaries as the final argument to func NewMessage?

Hidden CCR errors

I'm trying to connect to a diameter server which accepts only valid origin-host values and only one connection per value. I'm getting this kind of error CEA:

Capabilities-Exchange-Answer (CEA)
{Code:257,Flags:0x20,Version:0x1,Length:192,ApplicationId:0,HopByHopId:0x8a0654a7,EndToEndId:0xe3c8490d}
	Result-Code {Code:268,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{5012}}
	Origin-Host {Code:264,Flags:0x40,Length:20,VendorId:0,Value:DiameterIdentity{*hidden*},Padding:0}
	Origin-Realm {Code:296,Flags:0x40,Length:28,VendorId:0,Value:DiameterIdentity{*hidden*},Padding:0}
	Host-IP-Address {Code:257,Flags:0x40,Length:16,VendorId:0,Value:Address{*hidden*},Padding:2}
	Vendor-Id {Code:266,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{16247}}
	Product-Name {Code:269,Flags:0x0,Length:32,VendorId:0,Value:UTF8String{Diameter Control Agent},Padding:2}
	Origin-State-Id {Code:278,Flags:0x40,Length:12,VendorId:0,Value:Unsigned32{1484188053}}
	Error-Message {Code:281,Flags:0x0,Length:40,VendorId:0,Value:UTF8String{Origin-Host already connected},Padding:3}

At the moment cea.go will try to parse this and since it doesn't contain Auth-Application-ID I get error "ErrMissingApplication". I'd like to get access to the actual error for identifying different error cases. A simple way to do it in cea.go:

// Parse parses and validates the given message.
func (cea *CEA) Parse(m *diam.Message, localRole Role) (err error) {
...
...
	if _, err := app.Parse(m.Dictionary(), localRole); err != nil {
		if cea.ResultCode != diam.Success {
			return nil
		}
		return err
	}
...

with that I get an ErrFailedResultCode with the correct code.
The check for ResultCode could also be just after Unmarshal is called, maybe in sanityCheck. I'm not sure which way would be the best.

Padding is nonzero

When examining pcap files with Wireshark I see that i quite often get a warning saying AVP is malformed because padding is zero. I have noticed this issue only quite recently so could it be that some recent change is responsible for this?

Does Unmarshal support enums?

I tried to unmarshal message containing enum AVPs as int32 values but the value was always 0. The reflect_test.go doesn't contain examples of enum AVP types. Am I just doing something wrong or maybe there is some problem with enum types?

Extending dictionaries (3GPP TS 32.299)

I am trying to extend dictionaries with additional AVPs, in particular some from the 3GPP TS 32.299 (http://www.3gpp.org/DynaReport/32299.htm) specification. A forwarning: I am new to Go and relatively new to Diameter.

Based on the credit contol dictionary extension, I have tried to define additional library to be loaded in https://github.com/fiorix/go-diameter/blob/master/diam/dict/default.go and added xml to https://github.com/fiorix/go-diameter/tree/master/diam/dict/testdata. Unfortunately I wasn't able to compile the package due to following error:

gocode/src/github.com/fiorix/go-diameter/diam/dict/default.go:19: syntax error: unexpected name, expecting )
gocode/src/github.com/fiorix/go-diameter/diam/dict/default.go:942: syntax error: unexpected literal 3, expecting name or (

Which are the lines where extension was defined and loaded. I also tried extending autogen.sh in similar fashion with no luck. My first question is therefore: what is the proper way of defining new dictionary?

As a workaround I pushed additional AVP's to existing credit control dictionary and added relevant codes to https://github.com/fiorix/go-diameter/blob/master/diam/avp/codes.go

Which seems to work, client and server can both send and receive new AVP, however respective packages cannot be decoded in wireshark (and wireshark is able to decode these AVP's coming from different application). Extension is defined as follows:

<avp name="Service-Information" code="873" must="V,M" may="P" must-not="-" may-encrypt="N">
  <!-- TO DO: put reference information -->-Info
  <data type="Grouped">
    <rule avp="IMS-Information" required="false" max="1"/>
    <!--rule avp="PS-Information" required="false" max="1"/-->
  </data>
</avp>

<avp name="IMS-Information" code="876" must="V,M" may="P" must-not="-" may-encrypt="N">
  <!-- TO DO: put reference information -->
  <data type="Grouped">
    <rule avp="Called-Party-Address" required="false" max="1"/>
  </data>
</avp>

<avp name="Called-Party-Address" code="832" must="V,M" may="P" must-not="-" may-encrypt="N">
  <!-- TO DO: put reference information -->
  <data type="UTF8String"/>
</avp>

From the wireshark capture I see that Vendor Specific flag is not set, though it is defined in the dictionary so I guess this could be the reason. Could you please advise what is wrong with these definition, why vendor specific flag is not being set?

I could push some definitions from 3GPP TS 32.299 to your library (just a selected few I need so far) if you don't mind and advise the proper way to add these AVPs

CER-CEA problems

I don't know is RFC 6733 give some requirement about this. But in the real case, I'm facing that acct-application-id and auth-application-id share same id.

Example fail to build

# github.com/fiorix/go-diameter/examples/s6a_proxy/service
src/github.com/fiorix/go-diameter/examples/s6a_proxy/service/s6a_proxy.go:50:16: unknown field 'HostIPAddress' in struct literal of type sm.Settings

If AVP is not found in dictionary server disconnects the client

This seems like a very extreme measure. According to RFC 6733:

o A request with an unrecognized AVP is received with the 'M' bit
(Mandatory bit) set causes an answer to be sent with the Result-
Code AVP set to DIAMETER_AVP_UNSUPPORTED and the Failed-AVP AVP
containing the offending AVP.

This functionality can't be currently implemented if client is disconnected.

Ideally only AVPs present in the dictionary are parsed but when the message is forwarded again, also the missing AVPS are present. This might need considerable refactoring in the code? Simpler solution would be to at least return an error instead of disconnecting the client.

acct application 3 is not supported

I am using client example as a starting point to connect to remote charging system (which is based on https://github.com/RestComm/jdiameter). CER which is sent from the client side includes auth-application-id = 4, remote peer returns auth-application-id = 4 and acct-application-id = 3. Console logs acct application 3 is not supported and connection is not established.

It looks that go-diameter sm is not behaving correctly since both peers have application in common, but it looks that client expects to support all applications returned in CEA. As specified in RFC6733:

The sender of the Capabilities-Exchange-Answer
(CEA) SHOULD include all of its supported applications as a hint to
the receiver regarding all of its application capabilities.

EDIT: It looks that there are 2 issues here:

  1. Improper handling of CEA by the state machine
  2. Application ID 3 should be part of diameter base

Using Grouped Datatype

Hi @fiorix, firstly may thanks for this library. I just started using it today and I'm having some trouble with the Grouped datatype.

I added the following AVP definitions to the dictionary according to http://tools.ietf.org/html/rfc4006#section-8.46

<diameter>
  <application id="0">
    <avp name="Subscription-Id" code="443" must="M" may="P" must-not="V" may-encrypt="-">
      <data type="Grouped"/>
    </avp>

    <avp name="Subscription-Id-Type" code="450" must="M" may="P" must-not="V" may-encrypt="Y">
      <data type="Enumerated">
        <item code="0" name="END_USER_E164"/>
        <item code="1" name="END_USER_IMSI"/>
        <item code="2" name="END_USER_SIP_URI"/>
        <item code="3" name="END_USER_NAI"/>
      </data>
    </avp>

    <avp name="Subscription-Id-Data" code="444" must="M" may="P" must-not="V" may-encrypt="Y">
      <data type="UTF8String"/>
    </avp>

  </application>
</diameter>

In my client I've done this:

const (
  SubscriptionIdType = datatypes.Enumerated(0x00)
  SubscriptionIdData = datatypes.UTF8String("85560234567")
)

func NewClient(c diam.Conn) {
  m := diam.NewRequest(272, 0, nil)
  m.NewAVP("Subscription-Id", 0x40, 0x0, datatypes.Grouped([SubscriptionIdType, SubscriptionIdData]))

Just having a problem with the last line. How to make a NewAVP which is of type Grouped and contains the other two types?

do not support connecting to relay agent?

When I use the example client to connect the relay agent, it exits with error:
auth application 4294967295 is not supported

The CEA from relay agent includes the relay application id:
AVP: Auth-Application-Id(258) l=12 f=-M- val=Relay (4294967295)

The relay agent is very standard network entity in diameter network, so does go-diameter supports it?

Called-Station-Id AVP definition missing

Seems like only the rule is added but definition is missing which causes the application to fail processing a message that contains such AVP.

I wonder what process has been used to create the dictionary (xml) files, maybe there could be some other missing AVP definitions?

Message.WriteTo panics for uint32 before string in group AVP

Hi,

I have hit a panic on normal usage, adding vendor AVPs in a group and trying to send them over network.

Before trying to figure it out by myself, is there any easy/obvious fix for my issue?

Have done a simplified test for my issue here:
https://github.com/cgrates/go-diameter/blob/master/diam/message_test.go#L185

I should mention that adding string before int works just fine and also if int is the last one in group. The only issue is when string follows int.

Thanks in advance!
DanB

Server example is not handling all commands

When trying to send Credit Control message to server (go-diameter/examples/server/server.go) server generates error message:

Could not find preloaded Command with code 272

While error is self-explanatory it is not clear how it can be remedied. If server example is correct could you please provide an update to wiki how to properly extend server and client examples to handle CreditControl command?

Unsupported address family: 0x0008

When I receive a message I get an error as in subj.

Ethernet layer in wireshark says:

Type: IPv4 (0x0800)

IP layer is decoded as:

Internet Protocol Version 4, Src: 172.20.21.7, Dst: 172.20.17.37
0100 .... = Version: 4
.... 0101 = Header Length: 20 bytes
Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
Total Length: 580
Identification: 0xa4a1 (42145)
Flags: 0x02 (Don't Fragment)
Fragment offset: 0
Time to live: 62
Protocol: TCP (6)
Header checksum: 0x5a51 [validation disabled]
Source: 172.20.21.7
Destination: 172.20.17.37
[Source GeoIP: Unknown]
[Destination GeoIP: Unknown]

Deadlock issue.

Kind sir, a design question… Using the following logic for a single diameter client which is intended for repeated re-use, there's an issue where the call to c, err := cl.cli.Dial(cl.destinationAddress) never returns after it gets executed. The aim of doing this is to reconnect in the case of a network interruption, as cli.c is used for writing CCR messages to a peer. Perhaps this is a case of misuse. Could you please clarify if it should be possible to call cli.Dial multiple times? The definition of cli is further below.

func (cl *Client) connect() error {
    // Handle locking.
    utils.Logln(cl.debug, "connect(diameter://"+cl.DestinationHost()+"):", "dialling")
    c, err := cl.cli.Dial(cl.destinationAddress)
    if err == nil {
        utils.Logln(cl.debug, "connect(diameter://"+cl.DestinationHost()+"):", "acquiring lock")
        cl.mut.Lock()
        cl.c = c
        cl.mut.Unlock()
        utils.Logln(cl.debug, "connect(diameter://"+cl.DestinationHost()+"):", "released lock")
    }
    return err
}

func (cl *Client) Run() {
    // Make a persistent connection with back-off.
    backoff := time.Second
    for {
        utils.Logln(cl.debug, "connect(diameter://"+cl.DestinationHost()+"):", "attempting")
        err := cl.connect()
        if err != nil {
            utils.Logln(cl.debug, "connect(diameter-err://"+cl.DestinationHost()+"):", err)
            backoff *= 2
            if backoff > 20 {
                backoff = 20
            }
            time.Sleep(backoff)
            continue
        }
        utils.Logln(cl.debug, "connect(diameter-h):"+cl.DestinationHost()+"):", "handshake ok")
        backoff = 1
        // Wait until the server kicks us out or a write fails, requiring a
        // reconnect.
        select {
        case <-cl.c.(diam.CloseNotifier).CloseNotify():
            cl.mut.Lock()
            cl.c = nil
            cl.mut.Unlock()
            utils.Logln(cl.debug, "reconnect(diameter://"+cl.DestinationHost()+"):", "initiated")
        case <-cl.reconnect:
            cl.Close()
            utils.Logln(cl.debug, "reconnect(diameter://"+cl.DestinationHost()+"):", "initiated")
        }
        utils.Logln(cl.debug, "connect(diameter):"+cl.DestinationHost()+"):", "client disconnected - recreating connection")
    }
}
type Client struct {
    c                  diam.Conn
    cli                *sm.Client
    destinationAddress string
    destinationRealm   datatype.DiameterIdentity
    destinationHost    datatype.DiameterIdentity
    originRealm        datatype.DiameterIdentity
    originHost         datatype.DiameterIdentity
    productName        datatype.UTF8String
    reconnect          chan struct{}
    saveFunc           saveOperation
    Identifier         string
    db                 *sql.DB
    mut                *sync.RWMutex
    debug              bool
}
    smcli := &sm.Client{
        Dict:               dict.Default,
        Handler:            mux,
        MaxRetransmits:     3,
        RetransmitInterval: time.Second,
        EnableWatchdog:     true,
        WatchdogInterval:   5 * time.Second,
        AcctApplicationID: []*diam.AVP{diam.NewAVP(avp.AcctApplicationID, avp.Mbit, 0,
            AcctApplicationID),
        },
    }

    // Set message handlers.
    mux.HandleFunc("CCA", cl.ccaHandler)

    cl.cli = smcli

Thanks for your time & attention.

Failed to decode AVPs with Vendor-Id

When trying to decode a message that has AVPs with vendor-ids, that are in the default.go dictionary, ReadMessage errors out with "Failed to decode AVP: Could not find AVP XXX".

Verified by adding a test case in diam/message_test.go for a message with a Service-Information AVP (873). This is defined in default.go, but missing the vendor-id. This seems to be the case for all AVPs defined in the variable tgpprorfXML.

Proposed solution is adding vendor-id="10415" to each of these AVPs in default.go.
(PS Interim solution that worked for me for now was adding a corrected version of the entire tgpprorfXML section to my custom dictionary).

Will create a pull request for this fix.

Message AVPs are stored in a slice

Currently message AVPs are stored in a slice but wouldn't multi-key dictionary be more appropriate data structure? This would make finding of AVPs faster. Also removal of AVPs will be easier (which seems not to be supported at all).

Response from diameter is not validated

for a scenario where we have different avp agreements (Dictionary having different response/answer) for
Voice ccr/a , Data ccr/a , Event ccr/a.

1)found that currently code base does not validate the answer

iam planning to enhance the parser to hold commands for different request type and then use them to validate response/answer

any one faced such a scenario ..any other approach used ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.