// errorCEA sends an error answer indicating that the CER failed due to // an unsupported (acct/auth) application, and includes the AVP that // caused the failure in the message. func errorCEA(sm *StateMachine, c diam.Conn, m *diam.Message, cer *smparser.CER, failedAVP *diam.AVP) error { hostIP, _, err := net.SplitHostPort(c.LocalAddr().String()) if err != nil { return fmt.Errorf("failed to parse own ip %q: %s", c.LocalAddr(), err) } var a *diam.Message if failedAVP == cer.InbandSecurityID { a = m.Answer(diam.NoCommonSecurity) } else { a = m.Answer(diam.NoCommonApplication) } a.Header.CommandFlags |= diam.ErrorFlag a.NewAVP(avp.OriginHost, avp.Mbit, 0, sm.cfg.OriginHost) a.NewAVP(avp.OriginRealm, avp.Mbit, 0, sm.cfg.OriginRealm) a.NewAVP(avp.HostIPAddress, avp.Mbit, 0, datatype.Address(net.ParseIP(hostIP))) a.NewAVP(avp.VendorID, avp.Mbit, 0, sm.cfg.VendorID) a.NewAVP(avp.ProductName, 0, 0, sm.cfg.ProductName) if cer.OriginStateID != nil { a.AddAVP(cer.OriginStateID) } a.NewAVP(avp.FailedAVP, avp.Mbit, 0, &diam.GroupedAVP{ AVP: []*diam.AVP{failedAVP}, }) if sm.cfg.FirmwareRevision != 0 { a.NewAVP(avp.FirmwareRevision, avp.Mbit, 0, sm.cfg.FirmwareRevision) } _, err = a.WriteTo(c) return err }
func sendACR(c diam.Conn, cfg *sm.Settings, n int) { // Get this client's metadata from the connection object, // which is set by the state machine after the handshake. // It contains the peer's Origin-Host and Realm from the // CER/CEA handshake. We use it to populate the AVPs below. meta, ok := smpeer.FromContext(c.Context()) if !ok { log.Fatal("Client connection does not contain metadata") } var err error var m *diam.Message for i := 0; i < n; i++ { m = diam.NewRequest(diam.Accounting, 0, c.Dictionary()) m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(strconv.Itoa(i))) m.NewAVP(avp.OriginHost, avp.Mbit, 0, cfg.OriginHost) m.NewAVP(avp.OriginRealm, avp.Mbit, 0, cfg.OriginRealm) m.NewAVP(avp.DestinationRealm, avp.Mbit, 0, meta.OriginRealm) m.NewAVP(avp.AccountingRecordType, avp.Mbit, 0, eventRecord) m.NewAVP(avp.AccountingRecordNumber, avp.Mbit, 0, datatype.Unsigned32(i)) m.NewAVP(avp.DestinationHost, avp.Mbit, 0, meta.OriginHost) if _, err = m.WriteTo(c); err != nil { log.Fatal(err) } } }
// successCEA sends a success answer indicating that the CER was successfuly // parsed and accepted by the server. func successCEA(sm *StateMachine, c diam.Conn, m *diam.Message, cer *smparser.CER) error { hostIP, _, err := net.SplitHostPort(c.LocalAddr().String()) if err != nil { return fmt.Errorf("failed to parse own ip %q: %s", c.LocalAddr(), err) } a := m.Answer(diam.Success) a.NewAVP(avp.OriginHost, avp.Mbit, 0, sm.cfg.OriginHost) a.NewAVP(avp.OriginRealm, avp.Mbit, 0, sm.cfg.OriginRealm) a.NewAVP(avp.HostIPAddress, avp.Mbit, 0, datatype.Address(net.ParseIP(hostIP))) a.NewAVP(avp.VendorID, avp.Mbit, 0, sm.cfg.VendorID) a.NewAVP(avp.ProductName, 0, 0, sm.cfg.ProductName) if cer.OriginStateID != nil { a.AddAVP(cer.OriginStateID) } 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) } } if sm.cfg.FirmwareRevision != 0 { a.NewAVP(avp.FirmwareRevision, avp.Mbit, 0, sm.cfg.FirmwareRevision) } _, err = a.WriteTo(c) return err }
func (cli *Client) handshake(c diam.Conn) (diam.Conn, error) { ip, _, err := net.SplitHostPort(c.LocalAddr().String()) if err != nil { return nil, err } m := cli.makeCER(net.ParseIP(ip)) // Ignore CER, but not DWR. cli.Handler.mux.HandleFunc("CER", func(c diam.Conn, m *diam.Message) {}) // Handle CEA and DWA. errc := make(chan error) dwac := make(chan struct{}) cli.Handler.mux.Handle("CEA", handleCEA(cli.Handler, errc)) cli.Handler.mux.Handle("DWA", handshakeOK(handleDWA(cli.Handler, dwac))) for i := 0; i < (int(cli.MaxRetransmits) + 1); i++ { _, err := m.WriteTo(c) if err != nil { return nil, err } select { case err := <-errc: // Wait for CEA. if err != nil { close(errc) return nil, err } if cli.EnableWatchdog { go cli.watchdog(c, dwac) } return c, nil case <-time.After(cli.RetransmitInterval): } } c.Close() return nil, ErrHandshakeTimeout }
func (cli *Client) dwr(c diam.Conn, osid uint32, dwac chan struct{}) { m := cli.makeDWR(osid) for i := 0; i < (int(cli.MaxRetransmits) + 1); i++ { _, err := m.WriteTo(c) if err != nil { return } select { case <-dwac: return case <-time.After(cli.RetransmitInterval): } } // Watchdog failed, disconnect. c.Close() }
func sendHMR(c diam.Conn, cfg *sm.Settings) error { // Get this client's metadata from the connection object, // which is set by the state machine after the handshake. // It contains the peer's Origin-Host and Realm from the // CER/CEA handshake. We use it to populate the AVPs below. meta, ok := smpeer.FromContext(c.Context()) if !ok { return errors.New("peer metadata unavailable") } sid := "session;" + strconv.Itoa(int(rand.Uint32())) m := diam.NewRequest(helloMessage, helloApplication, nil) m.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String(sid)) m.NewAVP(avp.OriginHost, avp.Mbit, 0, cfg.OriginHost) m.NewAVP(avp.OriginRealm, avp.Mbit, 0, cfg.OriginRealm) m.NewAVP(avp.DestinationRealm, avp.Mbit, 0, meta.OriginRealm) m.NewAVP(avp.DestinationHost, avp.Mbit, 0, meta.OriginHost) m.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("foobar")) log.Printf("Sending HMR to %s\n%s", c.RemoteAddr(), m) _, err := m.WriteTo(c) return err }
// GetBridge returns the Bridge object for a given client, if it exists. // Otherwise GetBridge connects to the upstream server and set up the // bridge with the client, returning the newly created Bridge object. func GetBridge(c diam.Conn) *Bridge { liveMu.RLock() if b, ok := liveBridge[c.RemoteAddr().String()]; ok { liveMu.RUnlock() return b } liveMu.RUnlock() liveMu.Lock() defer liveMu.Unlock() b := &Bridge{ Client: make(chan *diam.Message), Server: make(chan *diam.Message), } liveBridge[c.RemoteAddr().String()] = b // Prepare for the upstream connection. mux := diam.NewServeMux() mux.HandleFunc("ALL", func(c diam.Conn, m *diam.Message) { // Forward incoming messages to the client. b.Client <- m }) // Connect to upstream server. s, err := diam.Dial(upstreamAddr, mux, nil) if err != nil { return nil } log.Printf("Creating bridge from %s to %s", c.RemoteAddr().String(), s.RemoteAddr().String()) go Pump(c, s, b.Client, b.Server) go Pump(s, c, b.Server, b.Client) return b }
func (self *DiameterAgent) handleALL(c diam.Conn, m *diam.Message) { utils.Logger.Warning(fmt.Sprintf("<DiameterAgent> Received unexpected message from %s:\n%s", c.RemoteAddr(), m)) }
func (self *DiameterAgent) handleCCR(c diam.Conn, m *diam.Message) { ccr, err := NewCCRFromDiameterMessage(m, self.cgrCfg.DiameterAgentCfg().DebitInterval) if err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Unmarshaling message: %s, error: %s", m, err)) return } var cca *CCA // For now we simply overload in loop, maybe we will find some other use of this for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { cca = self.processCCR(ccr, reqProcessor) if cca != nil && !reqProcessor.ContinueOnSuccess { break } } if cca == nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> No request processor enabled for CCR: %s, ignoring request", ccr.diamMessage)) return } if _, err := cca.AsDiameterMessage().WriteTo(c); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Failed to write message to %s: %s\n%s\n", c.RemoteAddr(), err, cca.AsDiameterMessage())) return } }
func (self *DiameterAgent) handlerCCR(c diam.Conn, m *diam.Message) { ccr, err := NewCCRFromDiameterMessage(m, self.cgrCfg.DiameterAgentCfg().DebitInterval) if err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Unmarshaling message: %s, error: %s", m, err)) return } cca := NewBareCCAFromCCR(ccr, self.cgrCfg.DiameterAgentCfg().OriginHost, self.cgrCfg.DiameterAgentCfg().OriginRealm) var processed, lclProcessed bool processorVars := make(map[string]string) // Shared between processors for _, reqProcessor := range self.cgrCfg.DiameterAgentCfg().RequestProcessors { lclProcessed, err = self.processCCR(ccr, reqProcessor, processorVars, cca) if lclProcessed { // Process local so we don't overwrite globally processed = lclProcessed } if err != nil || (lclProcessed && !reqProcessor.ContinueOnSuccess) { break } } if err != nil && err != ErrDiameterRatingFailed { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> CCA SetProcessorAVPs for message: %+v, error: %s", ccr.diamMessage, err)) return } else if !processed { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> No request processor enabled for CCR: %s, ignoring request", ccr.diamMessage)) return } self.connMux.Lock() defer self.connMux.Unlock() if _, err := cca.AsDiameterMessage().WriteTo(c); err != nil { utils.Logger.Err(fmt.Sprintf("<DiameterAgent> Failed to write message to %s: %s\n%s\n", c.RemoteAddr(), err, cca.AsDiameterMessage())) return } }
// ServeDIAM implements the diam.Handler interface. func (f handshakeOK) ServeDIAM(c diam.Conn, m *diam.Message) { if _, ok := smpeer.FromContext(c.Context()); ok { f(c, m) } }
// Pump messages from one side to the other. func Pump(src, dst diam.Conn, srcChan, dstChan chan *diam.Message) { for { select { case m := <-srcChan: if m == nil { src.Close() return } log.Printf( "Message from %s to %s\n%s", src.RemoteAddr().String(), dst.RemoteAddr().String(), m, ) if _, err := m.WriteTo(src); err != nil { src.Close() // triggers the case below } case <-src.(diam.CloseNotifier).CloseNotify(): liveMu.Lock() defer liveMu.Unlock() if _, ok := liveBridge[src.RemoteAddr().String()]; ok { delete(liveBridge, src.RemoteAddr().String()) log.Printf( "Destroying bridge from %s to %s", src.RemoteAddr().String(), dst.RemoteAddr().String(), ) } else { delete(liveBridge, dst.RemoteAddr().String()) log.Printf( "Destroying bridge from %s to %s", dst.RemoteAddr().String(), src.RemoteAddr().String(), ) } src.Close() dstChan <- nil return } } }
func handleALL(c diam.Conn, m *diam.Message) { log.Printf("Received unexpected message from %s:\n%s", c.RemoteAddr(), m) }