Navigation


Changeset 530:825a2992e3b9 in freeDiameter


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

Location:
extensions/app_radgw
Files:
2 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 */
  • extensions/app_radgw/rgwx_acct.c

    r526 r530  
    827827                               
    828828                                /* While here, we also add the Accouting-Record-Number AVP.
    829                                    We don't have a dedicated counter nor a state, so we just use the Diameter message End-to-end id here, which fits the conditions on the value. */
     829                                          The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
     830                                           and identifies this record within one session.  As Session-Id AVPs
     831                                           are globally unique, the combination of Session-Id and Accounting-
     832                                           Record-Number AVPs is also globally unique, and can be used in
     833                                           matching accounting records with confirmations.  An easy way to
     834                                           produce unique numbers is to set the value to 0 for records of type
     835                                           EVENT_RECORD and START_RECORD, and set the value to 1 for the first
     836                                           INTERIM_RECORD, 2 for the second, and so on until the value for
     837                                           STOP_RECORD is one more than for the last INTERIM_RECORD.
     838                                           
     839                                  -- we actually use the end-to-end id of the message here, which remains constant
     840                                    if we send a duplicate, so it has the same properties as the suggested algorithm.
     841                                    Anyway, it assumes that we are not converting twice the same RADIUS message.
     842                                   . */
    830843                                CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Number, 0, &avp ) );
    831844                                value.u32 = e2eid;
Note: See TracChangeset for help on using the changeset viewer.