/** * Send a AAAMessage asynchronously. * When the response is received, the callback_f(callback_param,...) is called. * @param message - the request to be sent * @param peer_id - FQDN of the peer to send * @param callback_f - callback to be called on transactional response or transaction timeout * @param callback_param - generic parameter to call the transactional callback function with * @returns 1 on success, 0 on failure * \todo remove peer_id and add Realm routing */ AAAReturnCode AAASendMessageToPeer( AAAMessage *message, str *peer_id, AAATransactionCallback_f *callback_f, void *callback_param) { peer *p; p = get_peer_by_fqdn(peer_id); if (!p) { LOG(L_ERR,"ERROR:AAASendMessageToPeer(): Peer unknown %.*s\n",peer_id->len,peer_id->s); goto error; } if (p->state!=I_Open && p->state!=R_Open){ LOG(L_ERR,"ERROR:AAASendMessageToPeer(): Peer not connected to %.*s\n",peer_id->len,peer_id->s); goto error; } /* only add transaction following when required */ if (callback_f){ if (is_req(message)) cdp_add_trans(message,callback_f,callback_param,config->transaction_timeout,1); else LOG(L_ERR,"ERROR:AAASendMessageToPeer(): can't add transaction callback for answer.\n"); } // if (!peer_send_msg(p,message)) if (!sm_process(p,Send_Message,message,0,0)) goto error; return 1; error: AAAFreeMessage(&message); return 0; }
int check_peer(str * peer_fqdn) { peer * p; p = get_peer_by_fqdn(peer_fqdn); if (p && !p->disabled && (p->state == I_Open || p->state == R_Open)) { return 1; } else { return -1; } }
/** * Get the first peer that is connected from the list of routing entries. * @param r - the list of routing entries to look into * @returns - the peer or null if none connected */ peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id) { peer *peers[LB_MAX_PEERS]; int peer_count=0; int prev_metric=0; routing_entry *i; peer *p; int j; time_t least_recent_time; LM_DBG("get_first_connected_route in list %p for app_id %d and vendor_id %d\n", r,app_id,vendor_id); for(i=r;i;i=i->next){ if (peer_count >= LB_MAX_PEERS) break; p = get_peer_by_fqdn(&(i->fqdn)); if (!p) LM_DBG("The peer %.*s does not seem to be connected or configured\n", i->fqdn.len,i->fqdn.s); else LM_DBG("The peer %.*s state is %s\n",i->fqdn.len,i->fqdn.s, (p->state==I_Open||p->state==R_Open)?"opened":"closed"); if (p && !p->disabled && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) { LM_DBG("The peer %.*s matches - will forward there\n",i->fqdn.len,i->fqdn.s); if (peer_count!=0) {//check the metric if (i->metric != prev_metric) break; //metric must be the same peers[peer_count++] = p; } else {//we're first prev_metric = i->metric; peers[peer_count++] = p; } } } if (peer_count==0) return 0; least_recent_time = peers[0]->last_selected; p = peers[0]; for (j=1; j<peer_count; j++) { if (peers[j]->last_selected < least_recent_time) { least_recent_time = peers[j]->last_selected; p = peers[j]; } } p->last_selected = time(NULL); return p; }
/** * Send a AAAMessage synchronously. * This blocks until a response is received or a transactional time-out happens. * @param message - the request to be sent * @param peer_id - FQDN of the peer to send * @returns 1 on success, 0 on failure * \todo remove peer_id and add Realm routing * \todo replace the busy-waiting lock in here with one that does not consume CPU */ AAAMessage* AAASendRecvMessageToPeer(AAAMessage *message, str *peer_id) { peer *p; gen_sem_t *sem; cdp_trans_t *t; AAAMessage *ans; p = get_peer_by_fqdn(peer_id); if (!p) { LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): Peer unknown %.*s\n",peer_id->len,peer_id->s); goto error; } if (p->state!=I_Open && p->state!=R_Open){ LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): Peer not connected to %.*s\n",peer_id->len,peer_id->s); goto error; } if (is_req(message)){ sem_new(sem,0); t = cdp_add_trans(message,sendrecv_cb,(void*)sem,config->transaction_timeout,0); // if (!peer_send_msg(p,message)) { if (!sm_process(p,Send_Message,message,0,0)){ sem_free(sem); goto error; } /* block until callback is executed */ while(sem_get(sem)<0){ if (shutdownx&&(*shutdownx)) goto error; LOG(L_WARN,"WARN:AAASendRecvMessageToPeer(): interrupted by signal or something > %s\n",strerror(errno)); } sem_free(sem); ans = t->ans; cdp_free_trans(t); return ans; } else { LOG(L_ERR,"ERROR:AAASendRecvMessageToPeer(): can't add wait for answer to answer.\n"); goto error; } error: out_of_memory: AAAFreeMessage(&message); return 0; }
static void cdp_rpc_enable_peer(rpc_t* rpc, void* ctx) { peer *cdp_peer; str peer_fqdn; if (rpc->scan(ctx, "S", &peer_fqdn) < 1) { rpc->fault(ctx, 400, "required peer fqdn argument"); return; } cdp_peer = get_peer_by_fqdn(&peer_fqdn); if (cdp_peer != NULL) { LM_DBG("Enabling CDP Peer: [%.*s]\n", peer_fqdn.len, peer_fqdn.s); cdp_peer->disabled = 0; return; } rpc->fault(ctx, 400, "peer not found"); return; }
void Send_ASA(cdp_session_t* s, AAAMessage* msg) { AAAMessage *asa; char x[4]; AAA_AVP *avp; LOG(L_INFO,"Send_ASA(): sending ASA\n"); if (!s) { //send an ASA for UNKNOWN_SESSION_ID - use AAASendMessage() // msg is the ASR received asa = AAANewMessage(IMS_ASA,0,0,msg); if (!asa) return; set_4bytes(x,AAA_SUCCESS); AAACreateAndAddAVPToMessage(asa,AVP_Result_Code,AAA_AVP_FLAG_MANDATORY,0,x,4); AAASendMessage(asa,0,0); }else{ // send... many cases... maybe not needed. // for now we do the same asa = AAANewMessage(IMS_ASA,0,0,msg); if (!asa) return; set_4bytes(x,AAA_SUCCESS); AAACreateAndAddAVPToMessage(asa,AVP_Result_Code,AAA_AVP_FLAG_MANDATORY,0,x,4); avp = AAAFindMatchingAVP(msg,0,AVP_Origin_Host,0,0); if (avp) { // This is because AAASendMessage is not going to find a route to the // the PCRF because TS 29.214 says no Destination-Host and no Auth-Application-Id // in the ASA LOG(L_INFO,"sending ASA to peer %.*s\n",avp->data.len,avp->data.s); peer *p; p = get_peer_by_fqdn(&avp->data); if (!peer_send_msg(p,asa)) { if (asa) AAAFreeMessage(&asa); //needed in frequency } else LOG(L_INFO,"success sending ASA\n"); }else if (!AAASendMessage(asa,0,0)) { LOG(L_ERR,"Send_ASA() : error sending ASA\n"); } } }
/** * Get the first peer that is connected from the list of routing entries. * @param r - the list of routing entries to look into * @returns - the peer or null if none connected */ peer* get_first_connected_route(routing_entry *r,int app_id,int vendor_id) { routing_entry *i; peer *p; LOG(L_DBG,"get_first_connected_route in list %p for app_id %d and vendor_id %d\n", r,app_id,vendor_id); for(i=r;i;i=i->next){ p = get_peer_by_fqdn(&(i->fqdn)); if (!p) LOG(L_DBG,"The peer %.*s does not seem to be connected or configured\n", i->fqdn.len,i->fqdn.s); else LOG(L_DBG,"The peer %.*s state is %s\n",i->fqdn.len,i->fqdn.s, (p->state==I_Open||p->state==R_Open)?"opened":"closed"); if (p && (p->state==I_Open || p->state==R_Open) && peer_handles_application(p,app_id,vendor_id)) { LOG(L_DBG,"The peer %.*s matches - will forward there\n",i->fqdn.len,i->fqdn.s); return p; } } return 0; }
/** * Get the first peer that is connected from the list of routing entries. * @param r - the list of routing entries to look into * @returns - the peer or null if none connected */ peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, int app_id, int vendor_id) { peer * peers[LB_MAX_PEERS]; int peer_count = 0; int prev_metric = 0; routing_entry *i; peer *p; int j; time_t least_recent_time; struct timespec time_spec; if (cdp_session) { /*try and find an already used peer for this session - sticky*/ if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) { //we have an old sticky peer. let's make sure it's up and connected before we use it. AAASessionsUnlock(cdp_session->hash); /*V1.1 - Don't attempt to hold two locks at same time */ p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn); AAASessionsLock(cdp_session->hash); /*V1.1 - As we were...no call seems to pass cdp_session unlocked */ if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { p->last_selected = time(NULL); LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s); return p; } } } for (i = r; i; i = i->next) { if (peer_count >= LB_MAX_PEERS) break; p = get_peer_by_fqdn(&(i->fqdn)); if (!p) LM_DBG("The peer %.*s does not seem to be connected or configured\n", i->fqdn.len, i->fqdn.s); else LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s, (p->state == I_Open || p->state == R_Open) ? "opened" : "closed"); if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s); if (peer_count != 0) {//check the metric if (i->metric != prev_metric) break; //metric must be the same peers[peer_count++] = p; } else {//we're first prev_metric = i->metric; peers[peer_count++] = p; } } } if (peer_count == 0) { return 0; } least_recent_time = peers[0]->last_selected; LM_DBG("peer [%.*s] was last used @ %ld\n", peers[0]->fqdn.len, peers[0]->fqdn.s, peers[0]->last_selected); p = peers[0]; for (j = 1; j < peer_count; j++) { LM_DBG("Peer [%.*s] was last used at [%ld]\n", peers[j]->fqdn.len, peers[j]->fqdn.s, peers[j]->last_selected); if (peers[j]->last_selected < least_recent_time) { least_recent_time = peers[j]->last_selected; p = peers[j]; } } ser_clock_gettime(&time_spec); p->last_selected = (time_spec.tv_sec*1000000) + round(time_spec.tv_nsec / 1.0e3); // Convert nanoseconds to microseconds LM_DBG("chosen peer [%.*s]\n", p->fqdn.len, p->fqdn.s); if (cdp_session) { if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) { LM_DBG("not enough storage for sticky peer - allocating more\n"); if (cdp_session->sticky_peer_fqdn.s) shm_free(cdp_session->sticky_peer_fqdn.s); cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1); if (!cdp_session->sticky_peer_fqdn.s) { LM_ERR("no more shm memory\n"); return 0; } cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1; memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1); } cdp_session->sticky_peer_fqdn.len = p->fqdn.len; memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len); } return p; }
/** * Get the first connect peer that matches the routing mechanisms. * - First the Destination-Host AVP value is tried if connected (the peer does not have to * be in the routing table at all). * - Then we look for a connected peer in the specific realm for the Destination-Realm AVP * - Then we look for the first connected peer in the default routes * @param m - the Diameter message to find the destination peer for * @returns - the connected peer or null if none connected found */ peer* get_routing_peer(cdp_session_t* cdp_session, AAAMessage *m) { str destination_realm = {0, 0}, destination_host = {0, 0}; AAA_AVP *avp, *avp_vendor, *avp2; AAA_AVP_LIST group; peer *p; routing_realm *rr; int app_id = 0, vendor_id = 0; LM_DBG("getting diameter routing peer for realm: [%.*s]\n", m->dest_realm->data.len, m->dest_realm->data.s); app_id = m->applicationId; avp = AAAFindMatchingAVP(m, 0, AVP_Vendor_Specific_Application_Id, 0, AAA_FORWARD_SEARCH); if (avp) { group = AAAUngroupAVPS(avp->data); avp_vendor = AAAFindMatchingAVPList(group, group.head, AVP_Vendor_Id, 0, 0); avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Auth_Application_Id, 0, 0); if (avp_vendor && avp2) { vendor_id = get_4bytes(avp_vendor->data.s); app_id = get_4bytes(avp2->data.s); } avp2 = AAAFindMatchingAVPList(group, group.head, AVP_Acct_Application_Id, 0, 0); if (avp_vendor && avp2) { vendor_id = get_4bytes(avp_vendor->data.s); app_id = get_4bytes(avp2->data.s); } AAAFreeAVPList(&group); } avp_vendor = AAAFindMatchingAVP(m, 0, AVP_Vendor_Id, 0, AAA_FORWARD_SEARCH); avp = AAAFindMatchingAVP(m, 0, AVP_Auth_Application_Id, 0, AAA_FORWARD_SEARCH); if (avp && avp_vendor) { vendor_id = get_4bytes(avp_vendor->data.s); app_id = get_4bytes(avp->data.s); } avp = AAAFindMatchingAVP(m, 0, AVP_Acct_Application_Id, 0, AAA_FORWARD_SEARCH); if (avp && avp_vendor) { vendor_id = get_4bytes(avp_vendor->data.s); app_id = get_4bytes(avp->data.s); } avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Host, 0, AAA_FORWARD_SEARCH); if (avp) destination_host = avp->data; if (destination_host.len) { /* There is a destination host present in the message try and route directly there */ p = get_peer_by_fqdn(&destination_host); if (p && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { p->last_selected = time(NULL); return p; } /* the destination host peer is not connected at the moment, try a normal route then */ } avp = AAAFindMatchingAVP(m, 0, AVP_Destination_Realm, 0, AAA_FORWARD_SEARCH); if (avp) destination_realm = avp->data; if (!config->r_table) { LM_ERR("get_routing_peer(): Empty routing table.\n"); return 0; } if (destination_realm.len) { /* first search for the destination realm */ for (rr = config->r_table->realms; rr; rr = rr->next) if (rr->realm.len == destination_realm.len && strncasecmp(rr->realm.s, destination_realm.s, destination_realm.len) == 0) break; if (rr) { p = get_first_connected_route(cdp_session, rr->routes, app_id, vendor_id); if (p) return p; else LM_ERR("get_routing_peer(): No connected Route peer found for Realm <%.*s>. Trying DefaultRoutes next...\n", destination_realm.len, destination_realm.s); } } /* if not found in the realms or no destination_realm, * get the first connected host in default routes */ LM_DBG("no routing peer found, trying default route\n"); p = get_first_connected_route(cdp_session, config->r_table->routes, app_id, vendor_id); if (!p) { LM_ERR("get_routing_peer(): No connected DefaultRoute peer found for app_id %d and vendor id %d.\n", app_id, vendor_id); } return p; }
/** * Get the first peer that is connected from the list of routing entries. * @param r - the list of routing entries to look into * @returns - the peer or null if none connected */ peer* get_first_connected_route(cdp_session_t* cdp_session, routing_entry *r, int app_id, int vendor_id) { peer * peers[LB_MAX_PEERS]; int peer_count = 0; int prev_metric = 0; routing_entry *i; peer *p; int j; time_t least_recent_time; if (cdp_session) { /*try and find an already used peer for this session - sticky*/ if ((cdp_session->sticky_peer_fqdn.len > 0) && cdp_session->sticky_peer_fqdn.s) { //we have an old sticky peer. let's make sure it's up and connected before we use it. p = get_peer_by_fqdn(&cdp_session->sticky_peer_fqdn); if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { p->last_selected = time(NULL); LM_DBG("Found a sticky peer [%.*s] for this session - re-using\n", p->fqdn.len, p->fqdn.s); return p; } } } for (i = r; i; i = i->next) { if (peer_count >= LB_MAX_PEERS) break; p = get_peer_by_fqdn(&(i->fqdn)); if (!p) LM_DBG("The peer %.*s does not seem to be connected or configured\n", i->fqdn.len, i->fqdn.s); else LM_DBG("The peer %.*s state is %s\n", i->fqdn.len, i->fqdn.s, (p->state == I_Open || p->state == R_Open) ? "opened" : "closed"); if (p && !p->disabled && (p->state == I_Open || p->state == R_Open) && peer_handles_application(p, app_id, vendor_id)) { LM_DBG("The peer %.*s matches - will forward there\n", i->fqdn.len, i->fqdn.s); if (peer_count != 0) {//check the metric if (i->metric != prev_metric) break; //metric must be the same peers[peer_count++] = p; } else {//we're first prev_metric = i->metric; peers[peer_count++] = p; } } } if (peer_count == 0) { return 0; } least_recent_time = peers[0]->last_selected; p = peers[0]; for (j = 1; j < peer_count; j++) { if (peers[j]->last_selected < least_recent_time) { least_recent_time = peers[j]->last_selected; p = peers[j]; } } if (cdp_session) { if (cdp_session->sticky_peer_fqdn_buflen <= p->fqdn.len) { LM_DBG("not enough storage for sticky peer - allocating more\n"); if (cdp_session->sticky_peer_fqdn.s) shm_free(cdp_session->sticky_peer_fqdn.s); cdp_session->sticky_peer_fqdn.s = (char*) shm_malloc(p->fqdn.len + 1); if (!cdp_session->sticky_peer_fqdn.s) { LM_ERR("no more shm memory\n"); return 0; } cdp_session->sticky_peer_fqdn_buflen = p->fqdn.len + 1; memset(cdp_session->sticky_peer_fqdn.s, 0, p->fqdn.len + 1); } cdp_session->sticky_peer_fqdn.len = p->fqdn.len; memcpy(cdp_session->sticky_peer_fqdn.s, p->fqdn.s, p->fqdn.len); } p->last_selected = time(NULL); return p; }