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
|
// SPDX-License-Identifier: GPL-3.0-or-later
package api
import (
"context"
"crypto/hmac"
"crypto/md5"
"fmt"
"net"
"strconv"
"time"
"layeh.com/radius"
"layeh.com/radius/rfc2869"
)
type Status struct {
AccessRequests int64 `stm:"access-requests"`
AccessAccepts int64 `stm:"access-accepts"`
AccessRejects int64 `stm:"access-rejects"`
AccessChallenges int64 `stm:"access-challenges"`
AuthResponses int64 `stm:"auth-responses"`
AuthDuplicateRequests int64 `stm:"auth-duplicate-requests"`
AuthMalformedRequests int64 `stm:"auth-malformed-requests"`
AuthInvalidRequests int64 `stm:"auth-invalid-requests"`
AuthDroppedRequests int64 `stm:"auth-dropped-requests"`
AuthUnknownTypes int64 `stm:"auth-unknown-types"`
AccountingRequests int64 `stm:"accounting-requests"`
AccountingResponses int64 `stm:"accounting-responses"`
AcctDuplicateRequests int64 `stm:"acct-duplicate-requests"`
AcctMalformedRequests int64 `stm:"acct-malformed-requests"`
AcctInvalidRequests int64 `stm:"acct-invalid-requests"`
AcctDroppedRequests int64 `stm:"acct-dropped-requests"`
AcctUnknownTypes int64 `stm:"acct-unknown-types"`
ProxyAccessRequests int64 `stm:"proxy-access-requests"`
ProxyAccessAccepts int64 `stm:"proxy-access-accepts"`
ProxyAccessRejects int64 `stm:"proxy-access-rejects"`
ProxyAccessChallenges int64 `stm:"proxy-access-challenges"`
ProxyAuthResponses int64 `stm:"proxy-auth-responses"`
ProxyAuthDuplicateRequests int64 `stm:"proxy-auth-duplicate-requests"`
ProxyAuthMalformedRequests int64 `stm:"proxy-auth-malformed-requests"`
ProxyAuthInvalidRequests int64 `stm:"proxy-auth-invalid-requests"`
ProxyAuthDroppedRequests int64 `stm:"proxy-auth-dropped-requests"`
ProxyAuthUnknownTypes int64 `stm:"proxy-auth-unknown-types"`
ProxyAccountingRequests int64 `stm:"proxy-accounting-requests"`
ProxyAccountingResponses int64 `stm:"proxy-accounting-responses"`
ProxyAcctDuplicateRequests int64 `stm:"proxy-acct-duplicate-requests"`
ProxyAcctMalformedRequests int64 `stm:"proxy-acct-malformed-requests"`
ProxyAcctInvalidRequests int64 `stm:"proxy-acct-invalid-requests"`
ProxyAcctDroppedRequests int64 `stm:"proxy-acct-dropped-requests"`
ProxyAcctUnknownTypes int64 `stm:"proxy-acct-unknown-types"`
}
type (
radiusClient interface {
Exchange(ctx context.Context, packet *radius.Packet, address string) (*radius.Packet, error)
}
Config struct {
Address string
Port int
Secret string
Timeout time.Duration
}
Client struct {
address string
secret string
timeout time.Duration
radiusClient
}
)
func New(conf Config) *Client {
return &Client{
address: net.JoinHostPort(conf.Address, strconv.Itoa(conf.Port)),
secret: conf.Secret,
timeout: conf.Timeout,
radiusClient: &radius.Client{Retry: time.Second, MaxPacketErrors: 10},
}
}
func (c Client) Status() (*Status, error) {
packet, err := newStatusServerPacket(c.secret)
if err != nil {
return nil, fmt.Errorf("error on creating StatusServer packet: %v", err)
}
resp, err := c.queryServer(packet)
if err != nil {
return nil, fmt.Errorf("error on request to '%s': %v", c.address, err)
}
return decodeResponse(resp), nil
}
func (c Client) queryServer(packet *radius.Packet) (*radius.Packet, error) {
ctx, cancel := context.WithTimeout(context.Background(), c.timeout)
defer cancel()
resp, err := c.Exchange(ctx, packet, c.address)
if err != nil {
return nil, err
}
if resp.Code != radius.CodeAccessAccept {
return nil, fmt.Errorf("'%s' returned response code %d", c.address, resp.Code)
}
return resp, nil
}
func newStatusServerPacket(secret string) (*radius.Packet, error) {
// https://wiki.freeradius.org/config/Status#status-of-freeradius-server
packet := radius.New(radius.CodeStatusServer, []byte(secret))
if err := FreeRADIUSStatisticsType_Set(packet, FreeRADIUSStatisticsType_Value_All); err != nil {
return nil, err
}
if err := rfc2869.MessageAuthenticator_Set(packet, make([]byte, 16)); err != nil {
return nil, err
}
hash := hmac.New(md5.New, packet.Secret)
encode, err := packet.Encode()
if err != nil {
return nil, err
}
if _, err := hash.Write(encode); err != nil {
return nil, err
}
if err := rfc2869.MessageAuthenticator_Set(packet, hash.Sum(nil)); err != nil {
return nil, err
}
return packet, nil
}
func decodeResponse(resp *radius.Packet) *Status {
return &Status{
AccessRequests: int64(FreeRADIUSTotalAccessRequests_Get(resp)),
AccessAccepts: int64(FreeRADIUSTotalAccessAccepts_Get(resp)),
AccessRejects: int64(FreeRADIUSTotalAccessRejects_Get(resp)),
AccessChallenges: int64(FreeRADIUSTotalAccessChallenges_Get(resp)),
AuthResponses: int64(FreeRADIUSTotalAuthResponses_Get(resp)),
AuthDuplicateRequests: int64(FreeRADIUSTotalAuthDuplicateRequests_Get(resp)),
AuthMalformedRequests: int64(FreeRADIUSTotalAuthMalformedRequests_Get(resp)),
AuthInvalidRequests: int64(FreeRADIUSTotalAuthInvalidRequests_Get(resp)),
AuthDroppedRequests: int64(FreeRADIUSTotalAuthDroppedRequests_Get(resp)),
AuthUnknownTypes: int64(FreeRADIUSTotalAuthUnknownTypes_Get(resp)),
AccountingRequests: int64(FreeRADIUSTotalAccountingRequests_Get(resp)),
AccountingResponses: int64(FreeRADIUSTotalAccountingResponses_Get(resp)),
AcctDuplicateRequests: int64(FreeRADIUSTotalAcctDuplicateRequests_Get(resp)),
AcctMalformedRequests: int64(FreeRADIUSTotalAcctMalformedRequests_Get(resp)),
AcctInvalidRequests: int64(FreeRADIUSTotalAcctInvalidRequests_Get(resp)),
AcctDroppedRequests: int64(FreeRADIUSTotalAcctDroppedRequests_Get(resp)),
AcctUnknownTypes: int64(FreeRADIUSTotalAcctUnknownTypes_Get(resp)),
ProxyAccessRequests: int64(FreeRADIUSTotalProxyAccessRequests_Get(resp)),
ProxyAccessAccepts: int64(FreeRADIUSTotalProxyAccessAccepts_Get(resp)),
ProxyAccessRejects: int64(FreeRADIUSTotalProxyAccessRejects_Get(resp)),
ProxyAccessChallenges: int64(FreeRADIUSTotalProxyAccessChallenges_Get(resp)),
ProxyAuthResponses: int64(FreeRADIUSTotalProxyAuthResponses_Get(resp)),
ProxyAuthDuplicateRequests: int64(FreeRADIUSTotalProxyAuthDuplicateRequests_Get(resp)),
ProxyAuthMalformedRequests: int64(FreeRADIUSTotalProxyAuthMalformedRequests_Get(resp)),
ProxyAuthInvalidRequests: int64(FreeRADIUSTotalProxyAuthInvalidRequests_Get(resp)),
ProxyAuthDroppedRequests: int64(FreeRADIUSTotalProxyAuthDroppedRequests_Get(resp)),
ProxyAuthUnknownTypes: int64(FreeRADIUSTotalProxyAuthUnknownTypes_Get(resp)),
ProxyAccountingRequests: int64(FreeRADIUSTotalProxyAccountingRequests_Get(resp)),
ProxyAccountingResponses: int64(FreeRADIUSTotalProxyAccountingResponses_Get(resp)),
ProxyAcctDuplicateRequests: int64(FreeRADIUSTotalProxyAcctDuplicateRequests_Get(resp)),
ProxyAcctMalformedRequests: int64(FreeRADIUSTotalProxyAcctMalformedRequests_Get(resp)),
ProxyAcctInvalidRequests: int64(FreeRADIUSTotalProxyAcctInvalidRequests_Get(resp)),
ProxyAcctDroppedRequests: int64(FreeRADIUSTotalProxyAcctDroppedRequests_Get(resp)),
ProxyAcctUnknownTypes: int64(FreeRADIUSTotalProxyAcctUnknownTypes_Get(resp)),
}
}
|