Navigation



Ignore:
Timestamp:
Sep 6, 2010, 2:28:53 PM (14 years ago)
Author:
Sebastien Decugis <sdecugis@nict.go.jp>
Branch:
default
Phase:
public
Message:

Improved duplicate detection in RADIUS/Diameter gw. It will be changed again soon

File:
1 edited

Legend:

Unmodified
Added
Removed
  • extensions/app_radgw/rgw_clients.c

    r521 r530  
    8282        }                       key;
    8383       
    84         /* information of previous msg received, for duplicate checks. */
     84        /* information of previous msg received, for duplicate checks -- we keep the last DUPLICATE_MESSAGES_BUFFER messages on each port. */
     85#define DUPLICATE_MESSAGES_BUFFER 200 /* This should actually be replaced with a time-based dynamic list! TODO... */
    8586        struct {
    86                 uint16_t                port;
    87                 uint8_t                 id;
    88                 uint8_t                 auth[16]; /* we also compare the request authenticator to avoid buggy NASes */
    89                 struct radius_msg *     ans; /* to be able to resend a lost answer */
    90         } last[2]; /*[0] for auth, [1] for acct. */
     87                int     cnt;   /* Counts the number of (different) requests we received */
     88                struct {
     89                        uint16_t        port;     /* The source UDP port of the request */
     90                        uint8_t         id;       /* The identifier in the request */
     91                        uint8_t         auth[16]; /* The request authenticator, because some NAS are not using identifier properly. */
     92                        struct radius_msg * ans;  /* When the answer has been sent already, keep it so we can send it back */
     93                        int             nbdup;    /* count the number of duplicate RADIUS requests we received on this message */
     94                } msg_info[DUPLICATE_MESSAGES_BUFFER];
     95        } duplicates_info[2]; /*[0] for auth, [1] for acct. */
    9196};
    9297
     
    169174                free(client->sa);
    170175                free(client->key.data);
     176               
     177                /* Free the duplicate info */
     178                for (idx=0; idx <= 1; idx++){
     179                        int i = 0;
     180                        for (i = 0; i < DUPLICATE_MESSAGES_BUFFER; i++) {
     181                                if (client->duplicates_info[idx].msg_info[i].ans) {
     182                                        /* Free this RADIUS message */
     183                                        radius_msg_free(client->duplicates_info[idx].msg_info[i].ans);
     184                                        free(client->duplicates_info[idx].msg_info[i].ans);
     185                                }
     186                        }
     187                }
     188               
    171189                free(client);
    172190        }
     
    269287int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli)
    270288{
    271         int idx;
     289        int p, i, dup = 0;
    272290       
    273291        TRACE_ENTRY("%p %p", msg, cli);
     
    276294       
    277295        if ((*msg)->serv_type == RGW_PLG_TYPE_AUTH)
    278                 idx = 0;
     296                p = 0;
    279297        else
    280                 idx = 1;
    281        
    282         if ((cli->last[idx].id == (*msg)->radius.hdr->identifier)
    283          && (cli->last[idx].port == (*msg)->port)
    284          && !memcmp(&cli->last[idx].auth[0], &(*msg)->radius.hdr->authenticator[0], 16)) {
    285                 /* Duplicate! */
    286                 TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu).", (*msg)->radius.hdr->identifier, ntohs((*msg)->port));
    287                 if (cli->last[idx].ans) {
    288                         /* Resend the answer */
    289                         CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type, cli->last[idx].ans->buf, cli->last[idx].ans->buf_used, cli->sa, (*msg)->port),  );
    290                 }
    291                 rgw_msg_free(msg);
    292         } else {
    293                 /* We have not just received this message already */
    294                 if (cli->last[idx].port == 0) { /* first message from this client */
    295                         /* Just add the new information */
    296                         ASSERT(cli->last[idx].ans == NULL);
    297                         cli->last[idx].id = (*msg)->radius.hdr->identifier;
    298                         cli->last[idx].port = (*msg)->port;
    299                         memcpy(&cli->last[idx].auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
    300                 } else {
    301                         /* We have got previous message(s), update the info only if answered already */
    302                         if (cli->last[idx].ans) {
    303                                 cli->last[idx].id = (*msg)->radius.hdr->identifier;
    304                                 cli->last[idx].port = (*msg)->port;
    305                                 memcpy(&cli->last[idx].auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
    306                                 /* Free the previous answer */
    307                                 radius_msg_free(cli->last[idx].ans);
    308                                 free(cli->last[idx].ans);
    309                                 cli->last[idx].ans = NULL;
    310                         }
    311                 }
     298                p = 1;
     299       
     300        /* Check in the previous DUPLICATE_MESSAGES_BUFFER messages if we have received the same identifier / authenticator / port combination */
     301        for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) {
     302                if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == (*msg)->radius.hdr->identifier)
     303                  && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == (*msg)->port)
     304                  && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &(*msg)->radius.hdr->authenticator[0], 16)) {
     305                        /* We already received this request */
     306                        cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].nbdup++;
     307                        dup = 1;
     308                        TRACE_DEBUG(INFO, "Received duplicated RADIUS message (id: %02hhx, port: %hu, dup #%d).",
     309                                        (*msg)->radius.hdr->identifier,
     310                                        ntohs((*msg)->port),
     311                                        cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].nbdup);
     312                        if (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans) {
     313                                /* Resend the answer */
     314                                CHECK_FCT_DO( rgw_servers_send((*msg)->serv_type,
     315                                                                cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans->buf,
     316                                                                cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans->buf_used,
     317                                                                cli->sa,
     318                                                                (*msg)->port),  );
     319                        }
     320                        rgw_msg_free(msg);
     321                        break;
     322                }
     323        }
     324       
     325        /* If we did no already receive this request, save it for later */
     326        if (!dup) {
     327                /* It's a new request, save its data */
     328                int i = cli->duplicates_info[p].cnt % DUPLICATE_MESSAGES_BUFFER;
     329               
     330                cli->duplicates_info[p].msg_info[i].port = (*msg)->port;
     331                cli->duplicates_info[p].msg_info[i].id   = (*msg)->radius.hdr->identifier;
     332                memcpy(&cli->duplicates_info[p].msg_info[i].auth[0], &(*msg)->radius.hdr->authenticator[0], 16);
     333                if (cli->duplicates_info[p].msg_info[i].ans) {
     334                        /* Free the old answer */
     335                        radius_msg_free(cli->duplicates_info[p].msg_info[i].ans);
     336                        free(cli->duplicates_info[p].msg_info[i].ans);
     337                        cli->duplicates_info[p].msg_info[i].ans = NULL;
     338                }
     339                cli->duplicates_info[p].msg_info[i].nbdup = 0;
     340                       
     341                cli->duplicates_info[p].cnt += 1;
    312342        }
    313343       
     
    800830int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli)
    801831{
    802         int idx;
     832        int p,i;
    803833       
    804834        TRACE_ENTRY("%p %p %p", msg, req, cli);
     
    828858        /* update the duplicate cache in rgw_clients */
    829859        if (req->serv_type == RGW_PLG_TYPE_AUTH)
    830                 idx = 0;
     860                p = 0;
    831861        else
    832                 idx = 1;
    833         if (cli->last[idx].ans) {
    834                 /* Free it */
    835                 radius_msg_free(cli->last[idx].ans);
    836                 free(cli->last[idx].ans);
    837         }
    838         cli->last[idx].ans = *msg;
    839         cli->last[idx].id = req->radius.hdr->identifier;
    840         cli->last[idx].port = req->port;
    841         *msg = NULL;
     862                p = 1;
     863        for (i = cli->duplicates_info[p].cnt - 1; i >= cli->duplicates_info[p].cnt - DUPLICATE_MESSAGES_BUFFER; i--) {
     864                /* Search the entry corresponding to the request */
     865                if ( (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].id == req->radius.hdr->identifier)
     866                  && (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].port == req->port)
     867                  && !memcmp(&cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].auth[0], &req->radius.hdr->authenticator[0], 16)) {
     868                        /* This should not happen, but just in case */
     869                        if (cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans) {
     870                                radius_msg_free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans);
     871                                free(cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans);
     872                        }
     873                        /* Now save the answer message */
     874                        cli->duplicates_info[p].msg_info[i % DUPLICATE_MESSAGES_BUFFER].ans = *msg;
     875                        *msg = NULL;
     876                        break;
     877                }
     878        }
     879       
     880        /* If we have not found the request in our circular buffer, it is probably too small */
     881        if (*msg) {
     882                TODO("Implement a dynamic list for RADIUS duplicates detection based on expiry time instead of number of messages");
     883                TRACE_DEBUG(INFO, "The circular buffer has circled before the Diameter answer was received, you should definitely increase DUPLICATE_MESSAGES_BUFFER value.");
     884                /* We don't re-save the value */
     885                radius_msg_free(*msg);
     886                free(*msg);
     887                *msg = NULL;
     888        }
    842889       
    843890        /* Finished */
Note: See TracChangeset for help on using the changeset viewer.