diff options
Diffstat (limited to 'develdoc.txt')
-rw-r--r-- | develdoc.txt | 279 |
1 files changed, 0 insertions, 279 deletions
diff --git a/develdoc.txt b/develdoc.txt deleted file mode 100644 index 05ee56e..0000000 --- a/develdoc.txt +++ /dev/null @@ -1,279 +0,0 @@ -radsecproxy documentation for developers - -1. Overall design - -At startup client and server configurations are read. Two lists -are created, called clconfs and srvconfs. Both contain clsrvconf -structs. - -For each server config, a client writer thread is created. This -takes care of sending requests to a server. - -Next for each known transport type which has a configured client, -we create listeners. Typically there is a default server port -that will be listened on, but multiple ports might be configured. -For each port there will normally be 1-2 sockets (IPv4 and/or IPv6). -For each socket a thread is created with the listener() defined for -the transport. - -This is all that happens in the main thread. The threads created -above need to take care of the rest. - -Client writers are generally responsible for sending messages to -clients, and if necessary creating and maintaining connections to -the client. Client writers create threads for handling replies from -servers. If connections are used, one thread is created for reading -from each connection. clientwr() will use connecter() and -clientconnreader() definitions for the transport. - -The listeners may receive RADIUS messages directly, which is the -case for UDP which is not connection based. Or they may receive -connections and create a new thread for handling each incoming -connection, where that thread will receive RADIUS messages. -The function receiving RADIUS client requests is generally called -xxxserverrd, where xxx is the transport name. The server reader -is responsible for creating a server writer thread that takes care -of sending RADIUS replies to a client. - -2. RADIUS message processing - -In 1 we described the threads used and the high level operations. -We will now describe how RADIUS messages are processed and flow -through the system. - -An incoming RADIUS request from a client is handled by a server -reader. The server reader calls newrequest() to obtain a request -struct. It sets rq->buf to point to the received message, rq->from -to point to the client struct for the client, and might for some -transports specify additional data. E.g. for UDP, the source port -and socket it was received on. The reader must make sure buf is -at least as large as the specified RADIUS message length. The -client reader calls radsrv(rq). When that returns it just waits -for the next message. - -radsrv() is in a way the core part of the proxy. It takes care -of validation, processing and routing of incoming requests. -It first creates a radmsg struct calling buf2radmsg(). This also -takes care of basic validation (e.g. that lengths add up) and -checking message authenticators. Unless it receives a valid -Access Request, Accounting Request or Status Server, it will -drop the request and return. - -It next calls addclientrq() which adds this request to a request -queue for rq->from. Before adding it to the queue, it checks if -it is a duplicate of something already in the queue. If a -duplicate, radsrv() drops the request and returns. - -Next radsrv() checks if it received a Status Server message. In -that case it responds with Access Accept and returns. - -Next it applies any rewritein rules and also checks TTL attribute. -If TTL expired, it will drop request and return. - -Next it looks for a User-Name attribute. If not present it will -will drop the request. However, if it is an accounting request -it will first send an accounting response. - -Next it calls findserver() to pick a server for sending the -message to. For this it will use the user-name attribute and the -realm definitions. It also takes into account which servers are -alive. - -If no server is found it will drop the message. However, in -certain cases it may send a reject or accounting response message. - -Next it reencrypts any attributes that are encrypted based on -the secrets of clients/servers. And after that, decrements TTL if -present, and applies any rewriteout rules. - -Finally radsrv() calls sendrq(rq) to pass the request to the -chosen server. - -sendrq() checks the request queue for a server. The request queue -is an array holding 256 entries, one for each possible message ID. -Normally it will start looking for a free slot at the ID after the -last entry inserted in the queue (0 follows 255). However in a -special case where sendrq() is called to send a status-server message, -it will always use ID 0. If status-server is enabled, ID 0 is not used -for other requests. If there are no free slots, the message is -discarded. - -When finding a free slot, it does, "to->requests[i].rq = rq" and -signals the writer thread : "pthread_cond_signal(&to->newrq_cond)". -After that, it returns, and the server reader thread can wait for a -new client request. - -We will now consider the client writer thread that takes care of -sending this request to a server. - -clientwr() continually looks for requests in its request buffer -and tries to send them to a server. It uses timers so that it can -sleep waiting for a new request, or sending status server, or -re-sending an existing request. When a new request comes in, it -will send it ASAP to the server and set tries to 1. For the -server there is a retryinterval timer. retryinterval seconds later -clientwr() will resend or remove the request. It is removed if the -server's retrycount parameter is exceeded (0 retries if reliable -transport). Status server messages are never resent. - -The handling of the request stops here, unless we get a reply. -We will now describe how replies are handled. - -Each transport has a function called something xxxclientrd() for -receiving replies from a server. This is run as a separate thread. -All they do is read a RADIUS message and call replyh(server, buf) -where server points to the server struct for the server, and buf -is a buffer large enough to contain the entire RADIUS message. It -will not read another message until replyh() returns. - -We will now consider replyh(). It will first check if there is an -outstanding request matching the id of the reply. This is done by -checking the request queue of the server. - -If it maches a request, it will validate and authenticate the -reply by calling buf2radmsg(). If this fails or the message type -is not one of Access Accept, Access Reject, Access Challenge or -Accounting Response, the reply is ignored. - -If the request was a status-server message, it simply removes -the request and returns. - -Next it will apply any rewritein rules and check TTL attribute if -present. If TTL is exceeded, the reply is ignored. - -Next it reencrypts some attributes with the secret of the client -the original request came from, and which the reply will be sent -back to. It also applies any rewriteout rules. - -Finally to pass the reply back to the client, it does -"rqout->rq->msg = msg" to store the reply message in the request, -and calls sendreply() with rqout->rq as parameter. When -sendreply() returns, we free the request from the server's -request queue. This also means that the ID can be used for a new -request. - -Now about sendreply(). All it does is basically to assemble the -reply message, take care of authenticators and set rq->replybuf -to point to the result. After that it adds a pointer to rq to -the clients reply queue and signals the server writer who is -responsible for sending replies to the client. - -The server writer is a separate thread created by the server reader, -typically called something like xxxserverwr. All it does is to send -whatever it finds in its replyq to the client and remove it. When -the queue is empty it waits for a signal from a sendreply(). - -The above shows the complete flow. It might be worth also looking a -bit more at the state created for each request though. - -As mentioned above, each request received from a client is stored in -request queue for the client. The request is stored in a request -struct which looks like this: - -struct request { - uint8_t *buf, *replybuf; - struct radmsg *msg; - ... -}; - -This request will remain in the queue until a new request is -received with the same id and which is not a duplicate. The -new one then replaces the previous. - -Initially for a new request, only buf is used (of the above specified -fields). Next the message is parsed and validated, and if ok, it is -stored in msg. buf with the request is freed. - -In sendrq() a request that is to be sent to a server, is again -reassembled from rq->msg into rq->buf. - -When a reply is received, it will again be parsed and validated, and -if ok, it will free the old rq->msg, and store the new instead. - -Finally, in sendreply() rq->replybuf is created from rq->msg, and -rq->msg is freed. rq->replybuf is kept so that if a duplicate request -is received later, we can just return rq->replybuf. - -rq->buf is removed by freerqoutdata(), because then we will not try -to send the request in rq->buf any more. - -Request structs should perhaps be freed when they "expire", rather -than wait until a new request with the same ID comes along. - -x. Transports - -struct protodefs protodefs[] contains definitions of the different -transport protocols. We will here describe the different parameters. - -struct protodefs { - char *name; -This should be a textual name for the transport, e.g. "udp". This is -used in client/server configurations and for debug/log messages. - - char *secretdefault; -Some transports like TLS that provides strong encryption, may have a -default RADIUS secret, since the RADIUS encryption is not needed. - - uint8_t socktype; -Typically set to SOCK_DGRAM or SOCK_STREAM. This is used when a -socket for the transport is created. - - char *portdefault; -The default server port for the transport, e.g. 1812. - - uint8_t retrycountdefault; -How many time a client request should be resent. For a reliable -transport like TCP/TLS, this should be 0. - - uint8_t retrycountmax; -The maximum allowed configurable value for retrycount. For reliable -transport it should probably be 0. - - uint8_t retryintervaldefault; -This is the default for how many seconds there should be between each -retry. For a reliable transport with 0 retries, this controls how -long it should wait for a reply to the client request. - - uint8_t retryintervalmax; -This is the maximum allowed retryinterval - - uint8_t duplicateintervaldefault; -This is the time period two requests with the same UDP source port -and request authenticator are considered duplicates. If a reply has -been sent to the first request, then that is resent. If no reply -has been sent, the second request is ignored. - - void *(*listener)(void*); -Each transport must define a listener function for the sockets -created by the transport. The socket is created by radsecproxy -core. If successful, the listener is called. - - int (*connecter)(struct server *, struct timeval *, int, char *); -When creating a new server, a clientwr() thread is created for sending -requests to the server. If a connecter() is defined for the transport, -clientwr() will call connecter() and exit if connecter() returns 0. - - void *(*clientconnreader)(void*); -If a connecter() is defined, then when that successfully returns, -a separate thread is created for reading from the connection. This -thread is responsible for handling replies from a server. - - int (*clientradput)(struct server *, unsigned char *); -Used by clientwr() for sending a RADIUS message. - - void (*addclient)(struct client *); -Need only be defined if need to override default client creation. -Used by UDP to have a common reply queue, rather than one per client. - - void (*addserverextra)(struct clsrvconf *); -Need only be defined if something needs to be done in addition to -the default server creation. - - uint8_t freesrcprotores; -Whether should free the resolver state for source ports and -addresses after initial startup. - - void (*initextra)(); -Can be defined it extra initialisation is needed for the transport. - -}; |