Hello, This is my first attempt at a module for OpenSER. This module is aimed at providing outbound (from OpenSER) priority/load-balancing for SRV records. I don't have a compilation environment at hand so I haven't tried to compile this code yet; I'm mostly looking for feedback on how well-behaved this code looks to people on the list, and whether there are SIP-related issues I didn't take care of.
The basic algorithm for srv_branches() is as follows: - from the message URI, obtain the host (domain) part and use it to do a SRV query; - use the records returned by the SRV query to build a list of branches, ordered by SRV priority (strict) and by somewhat-weighted randomized priority (if weights) or randomized priority (if all weights = 0). In a script, a call to srv_branches() would be followed by serialized_branches() in order to get the proper result; there is a snippet script at the top of the srv.c file. I can envision at least two enhancements to the module: (a) support hash (as in the dispatcher module) instead of using random() for target selection; (b) also support NAPTR -- but neither of these are critical to my application at this time. Thank you for your feedback. I am not subscribed to the list, so please Cc: me in your replies. Stéphane -- http://carrierclass.net/ http://www.linkedin.com/in/stephalnet
/** * $Id$ * * Copyright (C) 2007 Stephane Alnet ([EMAIL PROTECTED]) * * This file is part of openser, a free SIP server. * * openser is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version * * openser is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ipc.h> #include <unistd.h> #include <fcntl.h> #include <time.h> #include "../../parser/parse_uri.h" #include "../../dset.h" #include "../../resolve.h" #include "../../error.h" #include "../../mem/mem.h" /** * Usage: * * route[] * { * ... * setport("0"); # Force SRV resolution * srv_branches(); # Resolve SRV as branches * serialize_branches(); # Serialize the branches * t_relay(); * ... * } * * failure_route[1] * { * ... * next_branches(); # Next serialized branch * } * **/ MODULE_VERSION /** parameters */ /* int foo=4096; */ /** module functions */ static int mod_init(void); static int child_init(int); static int srv_branches(struct sip_msg*, char*, char*); static int srv_branches_fixup(void** param, int param_no); void destroy(void); static cmd_export_t cmds[]={ {"srv_branches", srv_branches, 0, srv_branches_fixup, REQUEST_ROUTE | FAILURE_ROUTE | ONREPLY_ROUTE | BRANCH_ROUTE}, {0,0,0,0,0} }; static param_export_t params[]={ /* {"foo", INT_PARAM, &foo}, */ {0,0,0} }; /** module exports */ struct module_exports exports= { "srv", cmds, params, 0, /* exported statistics */ mod_init, /* module initialization function */ (response_function) 0, (destroy_function) destroy, child_init /* per-child init function */ }; /** * init module function */ static int mod_init(void) { DBG("SRV: initializing ...\n"); return 0; } /** * Initialize children */ static int child_init(int rank) { DBG("SRV: init_child [%d] pid [%d]\n", rank, getpid()); return 0; } /* * q = MIN_Q ... MAX_Q (lower q = higher priority) * * rdata->priority = 0-65535 [RFC2782] lower value = higher priority (think MX) * rdata->weight = 0-65535 [RFC2782] algorithm: * To select a target to be contacted next, arrange all SRV RRs * (that have not been ordered yet) in any order, except that all * those with weight 0 are placed at the beginning of the list. * * Compute the sum of the weights of those RRs, and with each RR * associate the running sum in the selected order. Then choose a * uniform random number between 0 and the sum computed * (inclusive), and select the RR whose running sum value is the * first in the selected order which is greater than or equal to * the random number selected. The target host specified in the * selected SRV RR is the next one to be contacted by the client. * Remove this SRV RR from the set of the unordered SRV RRs and * apply the described algorithm to the unordered SRV RRs to select * the next target host. Continue the ordering process until there * are no unordered SRV RRs. This process is repeated for each * Priority. */ /* * Here we implement a slightly different algorithm. */ struct branch_def { str uri; unsigned short priority; /* from SRV */ unsigned short weight; /* from SRV */ unsigned order; /* for random selection */ /* order is either a random value, or weight*random value if weight > 0 */ }; /* */ int compare_branches( struct branch_def* a, struct branch_def* b ) { /* Lower priority = preferred */ if( a->priority < b->priority ) return -1; if( a->priority > b->priority ) return 1; /* Elements with 0 weight should be last */ if( a->weight == 0 && b->weight > 0 ) return 1; if( a->weight > 0 && b->weigth == 0 ) return -1; /* Either both are 0 weight or both are non-zero weight */ if( a->order < b->order ) return -1; if( a->order > b->order ) return 1; return 0; } /* Maximum number of SRV records we will consider */ #define MAX_BRANCHES 20 /* Should replace this with something similar to the hash functions in dispatcher */ static unsigned long random_msg(struct sip_msg* msg) { return random(); } static int srv_branches(struct sip_msg* msg, char* frm) { /* Should this be static or not? */ static branch_def branches[MAX_BRANCHES]; str luri; struct sip_uri uri; str name; struct rdata *head; struct rdata *rd; short ncount; /* Select the RURI to use for SRV resolution */ if (msg->new_uri.s) luri = msg->new_uri; else luri = msg->first_line.u.request.uri; if (luri.len > MAX_URI_SIZE - 1) { LOG(L_ERR, "ERROR: srv_branches: too long uri: %.*s\n", luri.len, luri.s); return -1; } if( parse_uri(luri.s,luri.len,&uri) < 0 ) { LOG(L_ERR, "ERROR: srv_branches: invalid uri: %.*s\n", luri.len, luri.s); return -1; } /* Check port == 0 */ if( uri.port_no != 0 ) { LOG(L_ERR, "ERROR: srv_branches: uri %.*s port is not zero\n", luri.len, luri.s); return -1; } /* Check proto */ if( uri.proto == PROTO_NONE ) { LOG(L_ERR, "ERROR: srv_branches: uri %.*s protocol is not known\n", luri.len, luri.s); return -1; } /* Get the domain name from the RURI */ name = uri.host; if (name->len >= MAX_DNS_NAME) { LOG(L_ERR, "ERROR:srv_branches: domain name too long\n"); return 0; } if ((name->len+SRV_MAX_PREFIX_LEN+1)>MAX_DNS_NAME) { LOG(L_WARN, "WARNING:srv_branches: domain name too long (%d)," " unable to perform SRV lookup\n", name->len); return 0; } /* Build the SRV name */ { char* tmp; tmp = pkg_malloc(MAX_DNS_NAME); if (!tmp){ LOG(L_ERR, "ERROR: srv_branches: memory allocation failure\n"); return E_OUT_OF_MEM; } memcpy(tmp, name->s, name->len); tmp[name->len] = '\0'; switch (*proto) { case PROTO_UDP: memcpy(tmp, SRV_UDP_PREFIX, SRV_UDP_PREFIX_LEN); memcpy(tmp+SRV_UDP_PREFIX_LEN, name->s, name->len); tmp[SRV_UDP_PREFIX_LEN + name->len] = '\0'; break; #ifdef USE_TCP case PROTO_TCP: if (tcp_disable) goto err_proto; memcpy(tmp, SRV_TCP_PREFIX, SRV_TCP_PREFIX_LEN); memcpy(tmp+SRV_TCP_PREFIX_LEN, name->s, name->len); tmp[SRV_TCP_PREFIX_LEN + name->len] = '\0'; break; #endif #ifdef USE_TLS case PROTO_TLS: if (tls_disable) goto err_proto; memcpy(tmp, SRV_TLS_PREFIX, SRV_TLS_PREFIX_LEN); memcpy(tmp+SRV_TLS_PREFIX_LEN, name->s, name->len); tmp[SRV_TLS_PREFIX_LEN + name->len] = '\0'; break; #endif default: LOG(L_ERR, "ERROR:sip_resolvehost: unsupported proto %d\n", *proto); return 0; } /* Resolve the SRV */ head = get_record(tmp,T_SRV); pkg_free(tmp); } ncount = 0; /* Build the branch records based on the SRV records */ { /* Go through the SRV records list */ for( rd=head ; rd ; rd=rd->next ) { unsigned end, len; struct srv_data *rdata; str new_host, new_port; str new_uri; char port_s[10]; if(rd->type!=T_SRV ) continue; /* should never happen */ rdata = (struct srv_data*)(rd->rdata); /* New host */ new_host.s = rdata->name; new_host.len = rdata->name_len; /* New port (as string) */ new_port.s = port_s; new_port.len = snprintf(new_port.s,9,"%d",rdata->port); /* Build new destination URI */ new_uri.len = 0; new_uri.s = pkg_malloc(MAX_URI_SIZE); if (!new_uri.s){ LOG(L_ERR, "ERROR: srv_branches: memory allocation failure\n"); pkg_free(new_port.s); return E_OUT_OF_MEM; } /* Inspired by the code for sethostport in action.c */ #define APPEND(X) \ len=strlen(X); if(new_uri.len+len>MAX_URI_SIZE-1) goto error_uri; \ memcpy(new_uri.s+new_uri.len,X,len); new_uri.len+=len; #define APPEND_str(X) \ len=X.len; if(new_uri.len+len>MAX_URI_SIZE-1) goto error_uri; \ memcpy(new_uri.s+new_uri.len,X.s,len); new_uri.len+=len; /* Copy username, password from URI */ APPEND("sip:") if(uri.user.s) { APPEND_str(uri.user) } if(uri.passwd.s) { APPEND(":") APPEND_str(uri.passwd) } if(uri.user.s) { APPEND("@") } /* Copy host and port from SRV */ APPEND_str(new_host) APPEND(":") APPEND_str(new_port) /* Copy others from URI */ if(uri.params.s) { APPEND(";") APPEND_str(uri.params) } if(uri.headers.s) { APPEND("?") APPEND_str(uri.headers) } new_uri.s[new_uri.len] = '\0'; branches[ncount].uri = new_uri; /* For now use priority plus a non-random order */ branches[ncount].priority = rdata->priority; branches[ncount].weight = rdata->weight; branches[ncount].order = random()%65536; if(rdata->weight != 0) branches[ncount].order *= rdata->weight; ncount++; continue; error_uri: pkg_free(new_uri.s); } free_rdata_list(head); } if( ncount == 0 ) { LOG(L_ERR, "ERROR: srv_branches: no valid SRV records for %.*s\n", name.len, name.s); return -1; } /* Sort the values */ qsort(branches,size_of(struct branch_def),MAX_BRANCHES,&compare_branches); /* Now we append the entries as branches. q are set in increasing order. */ for( q = 0; q < ncount; q++ ) { append_branch(msg,msg->uri,branches[q].uri,0,q+MIN_Q,0,msg->force_send_socket); pkg_free(branches[q].uri.s); } return 1; err_proto: LOG(L_ERR, "ERROR:srv_branches: protocol is disabled\n"); return 0; } /** * destroy function */ void destroy(void) { DBG("SRV: destroy module ...\n"); } static int srv_branches_fixup(void** param, int param_no) { return 0; }
_______________________________________________ Devel mailing list Devel@openser.org http://openser.org/cgi-bin/mailman/listinfo/devel