Add an error code.
[radsecproxy.git] / develdoc.txt
1 radsecproxy documentation for developers
2
3 1. Overall design
4
5 At startup client and server configurations are read. Two lists
6 are created, called clconfs and srvconfs. Both contain clsrvconf
7 structs.
8
9 For each server config, a client writer thread is created. This
10 takes care of sending requests to a server.
11
12 Next for each known transport type which has a configured client,
13 we create listeners. Typically there is a default server port
14 that will be listened on, but multiple ports might be configured.
15 For each port there will normally be 1-2 sockets (IPv4 and/or IPv6).
16 For each socket a thread is created with the listener() defined for
17 the transport.
18
19 This is all that happens in the main thread. The threads created
20 above need to take care of the rest.
21
22 Client writers are generally responsible for sending messages to
23 clients, and if necessary creating and maintaining connections to
24 the client. Client writers create threads for handling replies from
25 servers. If connections are used, one thread is created for reading
26 from each connection. clientwr() will use connecter() and
27 clientconnreader() definitions for the transport.
28
29 The listeners may receive RADIUS messages directly, which is the
30 case for UDP which is not connection based. Or they may receive
31 connections and create a new thread for handling each incoming
32 connection, where that thread will receive RADIUS messages.
33 The function receiving RADIUS client requests is generally called
34 xxxserverrd, where xxx is the transport name. The server reader
35 is responsible for creating a server writer thread that takes care
36 of sending RADIUS replies to a client.
37
38 2. RADIUS message processing
39
40 In 1 we described the threads used and the high level operations.
41 We will now describe how RADIUS messages are processed and flow
42 through the system.
43
44 An incoming RADIUS request from a client is handled by a server
45 reader. The server reader calls newrequest() to obtain a request
46 struct. It sets rq->buf to point to the received message, rq->from
47 to point to the client struct for the client, and might for some
48 transports specify additional data. E.g. for UDP, the source port
49 and socket it was received on. The reader must make sure buf is
50 at least as large as the specified RADIUS message length. The
51 client reader calls radsrv(rq). When that returns it just waits
52 for the next message.
53
54 radsrv() is in a way the core part of the proxy. It takes care
55 of validation, processing and routing of incoming requests.
56 It first creates a radmsg struct calling buf2radmsg(). This also
57 takes care of basic validation (e.g. that lengths add up) and
58 checking message authenticators. Unless it receives a valid
59 Access Request, Accounting Request or Status Server, it will
60 drop the request and return.
61
62 It next calls addclientrq() which adds this request to a request
63 queue for rq->from. Before adding it to the queue, it checks if
64 it is a duplicate of something already in the queue. If a
65 duplicate, radsrv() drops the request and returns.
66
67 Next radsrv() checks if it received a Status Server message. In
68 that case it responds with Access Accept and returns.
69
70 Next it applies any rewritein rules and also checks TTL attribute.
71 If TTL expired, it will drop request and return.
72
73 Next it looks for a User-Name attribute. If not present it will
74 will drop the request. However, if it is an accounting request
75 it will first send an accounting response.
76
77 Next it calls findserver() to pick a server for sending the
78 message to. For this it will use the user-name attribute and the
79 realm definitions. It also takes into account which servers are
80 alive.
81
82 If no server is found it will drop the message. However, in
83 certain cases it may send a reject or accounting response message.
84
85 Next it reencrypts any attributes that are encrypted based on
86 the secrets of clients/servers. And after that, decrements TTL if
87 present, and applies any rewriteout rules.
88
89 Finally radsrv() calls sendrq(rq) to pass the request to the
90 chosen server.
91
92 sendrq() checks the request queue for a server. The request queue
93 is an array holding 256 entries, one for each possible message ID.
94 Normally it will start looking for a free slot at the ID after the
95 last entry inserted in the queue (0 follows 255). However in a
96 special case where sendrq() is called to send a status-server message,
97 it will always use ID 0. If status-server is enabled, ID 0 is not used
98 for other requests. If there are no free slots, the message is
99 discarded.
100
101 When finding a free slot, it does, "to->requests[i].rq = rq" and
102 signals the writer thread : "pthread_cond_signal(&to->newrq_cond)".
103 After that, it returns, and the server reader thread can wait for a
104 new client request.
105
106 We will now consider the client writer thread that takes care of
107 sending this request to a server.
108
109 clientwr() continually looks for requests in its request buffer
110 and tries to send them to a server. It uses timers so that it can
111 sleep waiting for a new request, or sending status server, or
112 re-sending an existing request. When a new request comes in, it
113 will send it ASAP to the server and set tries to 1. For the
114 server there is a retryinterval timer. retryinterval seconds later
115 clientwr() will resend or remove the request. It is removed if the
116 server's retrycount parameter is exceeded (0 retries if reliable
117 transport). Status server messages are never resent.
118
119 The handling of the request stops here, unless we get a reply.
120 We will now describe how replies are handled.
121
122 Each transport has a function called something xxxclientrd() for
123 receiving replies from a server. This is run as a separate thread.
124 All they do is read a RADIUS message and call replyh(server, buf)
125 where server points to the server struct for the server, and buf
126 is a buffer large enough to contain the entire RADIUS message. It
127 will not read another message until replyh() returns.
128
129 We will now consider replyh(). It will first check if there is an
130 outstanding request matching the id of the reply. This is done by
131 checking the request queue of the server.
132
133 If it maches a request, it will validate and authenticate the
134 reply by calling buf2radmsg(). If this fails or the message type
135 is not one of Access Accept, Access Reject, Access Challenge or
136 Accounting Response, the reply is ignored.
137
138 If the request was a status-server message, it simply removes
139 the request and returns.
140
141 Next it will apply any rewritein rules and check TTL attribute if
142 present. If TTL is exceeded, the reply is ignored.
143
144 Next it reencrypts some attributes with the secret of the client
145 the original request came from, and which the reply will be sent
146 back to. It also applies any rewriteout rules.
147
148 Finally to pass the reply back to the client, it does
149 "rqout->rq->msg = msg" to store the reply message in the request,
150 and calls sendreply() with rqout->rq as parameter. When
151 sendreply() returns, we free the request from the server's
152 request queue. This also means that the ID can be used for a new
153 request.
154
155 Now about sendreply(). All it does is basically to assemble the
156 reply message, take care of authenticators and set rq->replybuf
157 to point to the result. After that it adds a pointer to rq to
158 the clients reply queue and signals the server writer who is
159 responsible for sending replies to the client.
160
161 The server writer is a separate thread created by the server reader,
162 typically called something like xxxserverwr. All it does is to send
163 whatever it finds in its replyq to the client and remove it. When
164 the queue is empty it waits for a signal from a sendreply().
165
166 The above shows the complete flow. It might be worth also looking a
167 bit more at the state created for each request though.
168
169 As mentioned above, each request received from a client is stored in
170 request queue for the client. The request is stored in a request
171 struct which looks like this:
172
173 struct request {
174     uint8_t *buf, *replybuf;
175     struct radmsg *msg;
176     ...
177 };
178
179 This request will remain in the queue until a new request is
180 received with the same id and which is not a duplicate. The
181 new one then replaces the previous.
182
183 Initially for a new request, only buf is used (of the above specified
184 fields). Next the message is parsed and validated, and if ok, it is
185 stored in msg. buf with the request is freed.
186
187 In sendrq() a request that is to be sent to a server, is again
188 reassembled from rq->msg into rq->buf.
189
190 When a reply is received, it will again be parsed and validated, and
191 if ok, it will free the old rq->msg, and store the new instead.
192
193 Finally, in sendreply() rq->replybuf is created from rq->msg, and
194 rq->msg is freed. rq->replybuf is kept so that if a duplicate request
195 is received later, we can just return rq->replybuf.
196
197 rq->buf is removed by freerqoutdata(), because then we will not try
198 to send the request in rq->buf any more.
199
200 Request structs should perhaps be freed when they "expire", rather
201 than wait until a new request with the same ID comes along.
202
203 x. Transports
204
205 struct protodefs protodefs[] contains definitions of the different
206 transport protocols. We will here describe the different parameters.
207
208 struct protodefs {
209     char *name;
210 This should be a textual name for the transport, e.g. "udp". This is
211 used in client/server configurations and for debug/log messages.
212     
213     char *secretdefault;
214 Some transports like TLS that provides strong encryption, may have a
215 default RADIUS secret, since the RADIUS encryption is not needed.
216     
217     uint8_t socktype;
218 Typically set to SOCK_DGRAM or SOCK_STREAM. This is used when a
219 socket for the transport is created.
220     
221     char *portdefault;
222 The default server port for the transport, e.g. 1812.
223     
224     uint8_t retrycountdefault;
225 How many time a client request should be resent. For a reliable
226 transport like TCP/TLS, this should be 0.
227     
228     uint8_t retrycountmax;
229 The maximum allowed configurable value for retrycount. For reliable
230 transport it should probably be 0.
231     
232     uint8_t retryintervaldefault;
233 This is the default for how many seconds there should be between each
234 retry. For a reliable transport with 0 retries, this controls how
235 long it should wait for a reply to the client request.
236     
237     uint8_t retryintervalmax;
238 This is the maximum allowed retryinterval
239     
240     uint8_t duplicateintervaldefault;
241 This is the time period two requests with the same UDP source port
242 and request authenticator are considered duplicates. If a reply has
243 been sent to the first request, then that is resent. If no reply
244 has been sent, the second request is ignored.
245     
246     void *(*listener)(void*);
247 Each transport must define a listener function for the sockets
248 created by the transport. The socket is created by radsecproxy
249 core. If successful, the listener is called.
250     
251     int (*connecter)(struct server *, struct timeval *, int, char *);
252 When creating a new server, a clientwr() thread is created for sending
253 requests to the server. If a connecter() is defined for the transport,
254 clientwr() will call connecter() and exit if connecter() returns 0.
255
256     void *(*clientconnreader)(void*);
257 If a connecter() is defined, then when that successfully returns,
258 a separate thread is created for reading from the connection. This
259 thread is responsible for handling replies from a server.
260
261     int (*clientradput)(struct server *, unsigned char *);
262 Used by clientwr() for sending a RADIUS message.
263
264     void (*addclient)(struct client *);
265 Need only be defined if need to override default client creation.
266 Used by UDP to have a common reply queue, rather than one per client.
267
268     void (*addserverextra)(struct clsrvconf *);
269 Need only be defined if something needs to be done in addition to
270 the default server creation.
271     
272     uint8_t freesrcprotores;
273 Whether should free the resolver state for source ports and
274 addresses after initial startup.
275
276     void (*initextra)();
277 Can be defined it extra initialisation is needed for the transport.
278
279 };