Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sctp multihoming support #2077

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions cfg.lex
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ LOGSTDERROR log_stderror
LOGFACILITY log_facility
LOGNAME log_name
LISTEN listen
LISTEN_SCTP_SEC listen_sctp_sec
SCTP_SEND_TTL_MS sctp_send_ttl_ms
MEMGROUP mem-group
ALIAS alias
AUTO_ALIASES auto_aliases
Expand Down Expand Up @@ -417,6 +419,8 @@ SPACE [ ]
<INITIAL>{LOGFACILITY} { yylval.strval=yytext; return LOGFACILITY; }
<INITIAL>{LOGNAME} { yylval.strval=yytext; return LOGNAME; }
<INITIAL>{LISTEN} { count(); yylval.strval=yytext; return LISTEN; }
<INITIAL>{LISTEN_SCTP_SEC} { count(); yylval.strval=yytext; return LISTEN_SCTP_SEC; }
<INITIAL>{SCTP_SEND_TTL_MS} { count(); yylval.strval=yytext; return SCTP_SEND_TTL_MS; }
<INITIAL>{MEMGROUP} { count(); yylval.strval=yytext; return MEMGROUP; }
<INITIAL>{ALIAS} { count(); yylval.strval=yytext; return ALIAS; }
<INITIAL>{AUTO_ALIASES} { count(); yylval.strval=yytext; return AUTO_ALIASES; }
Expand Down
13 changes: 13 additions & 0 deletions cfg.y
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ extern int cfg_parse_only_routes;
%token LOGNAME
%token AVP_ALIASES
%token LISTEN
%token LISTEN_SCTP_SEC
%token SCTP_SEND_TTL_MS
%token MEMGROUP
%token ALIAS
%token AUTO_ALIASES
Expand Down Expand Up @@ -1137,6 +1139,17 @@ assign_stm: DEBUG EQUAL snumber
| LISTEN EQUAL error { yyerror("ip address or hostname "
"expected (use quotes if the hostname includes"
" config keywords)"); }
| LISTEN_SCTP_SEC EQUAL ip {
if (init_su(&sctp_sec_addr, $3, 0)!=0){
LM_CRIT("cfg. parser: failed"
" to add secondary sctp address\n");
break;
}
}
| LISTEN_SCTP_SEC EQUAL error { yyerror("ip address expected"); }
| SCTP_SEND_TTL_MS EQUAL NUMBER { IFOR();
sctp_send_ttl_ms=$3; }
| SCTP_SEND_TTL_MS EQUAL error { yyerror("number expected"); }
| MEMGROUP EQUAL STRING COLON multi_string { IFOR();
/* convert STIRNG ($3) to an ID */
/* update the memstats type for each module */
Expand Down
3 changes: 3 additions & 0 deletions globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,7 @@ extern int disable_503_translation;

extern int enable_asserts;
extern int abort_on_assert;

extern union sockaddr_union sctp_sec_addr;
extern uint32_t sctp_send_ttl_ms;
#endif
2 changes: 2 additions & 0 deletions main.c
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,8 @@ int is_main = 1; /* flag = is this the "main" process? */
char* pid_file = 0; /* filename as asked by user */
char* pgid_file = 0;

union sockaddr_union sctp_sec_addr = { .s.sa_family = 0 };
uint32_t sctp_send_ttl_ms = 0;

/**
* Clean up on exit. This should be called before exiting.
Expand Down
128 changes: 117 additions & 11 deletions modules/proto_sctp/sctp_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <linux/types.h>
#include <linux/errqueue.h>
#endif
#include <fcntl.h>

#include "../../mem/shm_mem.h"
#include "../../sr_module.h"
Expand Down Expand Up @@ -89,16 +90,32 @@ int proto_sctp_init_listener(struct socket_info* sock_info)
}
#endif

#ifdef SCTP_EVENTS
struct sctp_event_subscribe ev_s = {0};
ev_s.sctp_association_event = 1;
ev_s.sctp_send_failure_event = 1;

if(setsockopt(sock_info->socket, IPPROTO_SCTP, SCTP_EVENTS, &ev_s, sizeof(ev_s)) == -1) {
LM_WARN("setsockopt SCTP_EVENTS: %s (%d)\n",
strerror(errno), errno);
}
#endif

/* this sockopt causes a kernel panic in some sctp implementations.
* commenting it out. -gmarmon */

/* tos
optval=tos;
if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval,
sizeof(optval)) ==-1){
LM_WARN("setsockopt tos: %s\n", strerror(errno));
if (addr->s.sa_family == AF_INET) {
if (setsockopt(sock_info->socket, IPPROTO_IP, IP_TOS, (void*)&optval,
sizeof(optval)) ==-1){
LM_WARN("setsockopt tos: %s\n", strerror(errno));
}
} else if (addr->s.sa_family == AF_INET6) {
if (setsockopt(sock_info->socket, IPPROTO_IPV6, IPV6_TCLASS, (void*)&optval,
sizeof(optval)) ==-1){
LM_WARN("setsockopt v6 tos: %s\n", strerror(errno));
}
}
*/

#if defined (__linux__) && defined(SCTP_ERRORS)
/* will SCTP_ERRORS ever be defined? -gmarmon */
Expand All @@ -125,6 +142,30 @@ int proto_sctp_init_listener(struct socket_info* sock_info)
" local address, try site local or global\n");
goto error;
}
if (sctp_sec_addr.s.sa_family != 0) {
if (sctp_bindx(sock_info->socket,
(struct sockaddr *)&sctp_sec_addr, 1,
SCTP_BINDX_ADD_ADDR) == -1)
LM_ERR("bindx(%x, %p) : %s\n",
sock_info->socket, &sctp_sec_addr.s,
strerror(errno));
else
LM_INFO("sctp bindx success to: %s\n",
inet_ntoa(((struct sockaddr_in *)&sctp_sec_addr)->sin_addr));
}

/* make non-blocking sending */
optval=fcntl(sock_info->socket, F_GETFL);
if (optval==-1){
LM_ERR("fnctl failed with %s [%d]\n", strerror(errno), errno);
goto error;
}
if (fcntl(sock_info->socket, F_SETFL, optval|O_NONBLOCK)==-1){
LM_ERR("fcntl: set non-blocking failed with %s [%d]\n",
strerror(errno), errno);
goto error;
}

if(listen(sock_info->socket, LISTEN_BACKLOG)<0){
LM_ERR("listen(%x, %d) on %s: %s\n",
sock_info->socket,
Expand All @@ -139,19 +180,45 @@ int proto_sctp_init_listener(struct socket_info* sock_info)
return -1;
}

static char *sctp_assoc_change_state2s(short int state)
{
char *s;

switch(state) {
case SCTP_COMM_UP:
s = "SCTP_COMM_UP";
break;
case SCTP_COMM_LOST:
s = "SCTP_COMM_LOST";
break;
case SCTP_RESTART:
s = "SCTP_RESTART";
break;
case SCTP_SHUTDOWN_COMP:
s = "SCTP_SHUTDOWN_COMP";
break;
case SCTP_CANT_STR_ASSOC:
s = "SCTP_CANT_STR_ASSOC";
break;
default:
s = "UNKNOWN";
break;
};
return s;
}

int proto_sctp_read(struct socket_info *si, int* bytes_read)
{
struct receive_info ri;
int len;
int len, msg_flags = 0;
static char buf [BUF_SIZE+1];
char *tmp;
unsigned int fromlen;
struct sctp_sndrcvinfo sinfo;

fromlen=sockaddru_len(si->su);
len = sctp_recvmsg(si->socket, buf, BUF_SIZE, &ri.src_su.s, &fromlen,
&sinfo, 0);
&sinfo, &msg_flags);
if (len==-1){
if (errno==EAGAIN){
LM_DBG("packet with bad checksum received\n");
Expand All @@ -163,6 +230,43 @@ int proto_sctp_read(struct socket_info *si, int* bytes_read)
return -2;
}

if (msg_flags & MSG_NOTIFICATION) {
union sctp_notification *snp = (union sctp_notification *)buf;

switch(snp->sn_header.sn_type) {
case SCTP_ASSOC_CHANGE:
su2ip_addr(&ri.src_ip, &ri.src_su);
if (snp->sn_assoc_change.sac_state == SCTP_COMM_UP)
LM_NOTICE("SCTP_ASSOC_CHANGE assoc_id: %d, peer ip:%s,"
"peer port:%d, state: %s\n",
snp->sn_assoc_change.sac_assoc_id,
ip_addr2a(&ri.src_ip),
su_getport(&ri.src_su),
sctp_assoc_change_state2s(snp->sn_assoc_change.sac_state));
else
LM_ERR("SCTP_ASSOC_CHANGE assoc_id: %d, peer ip:%s, "
"peer port:%d, state: %s\n",
snp->sn_assoc_change.sac_assoc_id,
ip_addr2a(&ri.src_ip),
su_getport(&ri.src_su),
sctp_assoc_change_state2s(snp->sn_assoc_change.sac_state));
break;
case SCTP_SEND_FAILED:
su2ip_addr(&ri.src_ip, &ri.src_su);
LM_ERR("SCTP_SEND_FAILED assoc_id: %d, peer ip:%s, "
"peer port:%d, error: %d\n",
snp->sn_send_failed.ssf_assoc_id,
ip_addr2a(&ri.src_ip),
su_getport(&ri.src_su),
snp->sn_send_failed.ssf_error);
break;
default:
LM_INFO("unexpected sctp notification type: %d\n",
snp->sn_header.sn_type);
}
return 0;
}

/* we must 0-term the messages, receive_msg expects it */
buf[len]=0; /* no need to save the previous char */

Expand Down Expand Up @@ -197,19 +301,21 @@ int proto_sctp_send(struct socket_info *source, char *buf, unsigned len,

tolen=sockaddru_len(*to);
again:
n=sctp_sendmsg(source->socket, buf, len, &to->s, tolen, 0, 0, 0, 0, 0);
n=sctp_sendmsg(source->socket, buf, len, &to->s, tolen, 0, SCTP_UNORDERED, 0, sctp_send_ttl_ms, 0);
#ifdef XL_DEBUG
LM_INFO("send status: %d\n", n);
#endif
if (n==-1){
LM_ERR("sctp_sendmsg(sock,%p,%d,%p,%d,0,0,0,0,0): %s(%d)\n",
buf,len,&to->s,tolen, strerror(errno),errno);

if (errno==EINTR) goto again;
if (errno==EINVAL) {
LM_CRIT("invalid sendtoparameters\n"
"one possible reason is the server is bound to localhost and\n"
"attempts to send to the net\n");
} else if(errno == EAGAIN || errno == EWOULDBLOCK) {
LM_ERR(L_ERR, "sctp_sendmsg failed, send buffer full\n");
} else {
LM_ERR("sctp_sendmsg(sock,%p,%d,%p,%d...): %s(%d)\n",
buf,len,&to->s,tolen, strerror(errno),errno);
}
}
return n;
Expand Down
83 changes: 81 additions & 2 deletions modules/sip_i/isup.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,27 @@ static struct isup_subfield calling_party_num_subf[] = {
{str_init("Address signal"), {0, {{0, 0}}, {0}}},
SUBF_INIT_EMPTY};

static struct isup_subfield generic_num_subf[] = {
{str_init("Number qualifier indicator"), {3,
{str_init("additional called party num"), str_init("additional connected num"),
str_init("additional calling party num")}, {1,5,6}}},
{str_init("Odd/even indicator"), {2,
{str_init("even"), str_init("odd")}, {0,1}}},
{str_init("Nature of address indicator"), {4,
{str_init("subscriber"), str_init("unknown"), str_init("national"),
str_init("international")}, {1,2,3,4}}},
{str_init("Number Incomplete indicator"), {2,
{str_init("complete"), str_init("incomplete")}, {0,1}}},
{str_init("Numbering plan indicator"), {3,
{str_init("ISDN"), str_init("Data"), str_init("Telex")}, {1,3,4}}},
{str_init("Address presentation restricted indicator"), {4,
{str_init("allowed"), str_init("restricted"), str_init("not available"),
str_init("reserved")}, {0,1,2,3}}},
{str_init("Screening indicator"), {2,
{str_init("user"), str_init("network")}, {1,3}}},
{str_init("Address signal"), {0, {{0, 0}}, {0}}},
SUBF_INIT_EMPTY};

static struct isup_subfield backward_call_ind_subf[] = {
{str_init("Charge indicator"), {2,
{str_init("no indication"), str_init("no charge")}, {0,1}}},
Expand Down Expand Up @@ -745,6 +766,64 @@ int calling_party_num_writef(int param_idx, int subfield_idx, unsigned char *par
return 0;
}

void generic_num_parsef(int subfield_idx, unsigned char *param_val, int len,
int *int_res, str *str_res)
{
int idx[] = {0,1,1,2,2,2,2};
int shift[] = {0,7,0,7,4,2,0};
int mask[] = {0xff,1,0x7f,1,7,3,3};
int oddeven = (param_val[1] >> 7) & 0x1;

if (subfield_idx < 0 || subfield_idx > 7) {
LM_ERR("BUG - bad subfield\n");
return;
}

switch (subfield_idx) {
case 1:
*int_res = oddeven;
break;
case 7:
isup_get_number(str_res, param_val + 3, len - 3, oddeven);
break;
default:
*int_res = (param_val[idx[subfield_idx]] >> shift[subfield_idx]) & mask[subfield_idx];
}
}

int generic_num_writef(int param_idx, int subfield_idx, unsigned char *param_val, int *len,
pv_value_t *val)
{
int new_val;
int num_len, oddeven;
str num;
int idx[] = {0,1,1,2,2,2,2};
int mask[] = {0xff,0x80,0x7f,0x80,0x70,0xc,0x3};
int shift[] = {0,7,0,7,4,2,0};

NUM_PARAM_GET_VAL_PV(7);

if (subfield_idx < 0 || subfield_idx > 7) {
LM_ERR("BUG - bad subfield\n");
return -1;
}

if (subfield_idx == 7) {
isup_put_number(param_val + 2, num, &num_len, &oddeven);
/* also set oddeven, just in case it wasn't already */
param_val[1] = SET_BITS(param_val[1], 0x80, 7, oddeven);
} else
param_val[idx[subfield_idx]] = SET_BITS(param_val[idx[subfield_idx]],
mask[subfield_idx], shift[subfield_idx], new_val);

if (subfield_idx == 7)
*len = num_len + 2;
else if (*len == 0)
*len = 2;

return 0;
}

void backward_call_ind_parsef(int subfield_idx, unsigned char *param_val, int len,
int *int_res, str *str_res)
{
Expand Down Expand Up @@ -1144,7 +1223,7 @@ struct isup_param_data isup_params[NO_ISUP_PARAMS] = {
{ISUP_PARM_CORRELATION_ID, str_init("Correlation id"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_SCF_ID, str_init("SCF id"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_CALL_DIVERSION_TREATMENT_IND, str_init("Call diversion treatment indicators"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_CALLED_IN_NUMBER, str_init("Called IN number"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_CALLED_IN_NUMBER, str_init("Called IN number"), original_called_num_parsef, original_called_num_writef, original_called_num_subf, NULL, 0},
{ISUP_PARM_CALL_OFFERING_TREATMENT_IND, str_init("Call offering treatment indicators"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_CHARGED_PARTY_IDENT, str_init("Charged party identification"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_CONFERENCE_TREATMENT_IND, str_init("Conference treatment indicators"), NULL, NULL, NULL, NULL, 0},
Expand All @@ -1171,7 +1250,7 @@ struct isup_param_data isup_params[NO_ISUP_PARAMS] = {
{ISUP_PARM_REDIRECT_FORWARD_INFO, str_init("Redirect forward information"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_REDIRECT_BACKWARD_INFO, str_init("Redirect backward information"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_NUM_PORTABILITY_FORWARD_INFO, str_init("Number portability forward information"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_GENERIC_ADDR, str_init("Generic Number"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_GENERIC_ADDR, str_init("Generic Number"), generic_num_parsef, generic_num_writef, generic_num_subf, NULL, 0},
{ISUP_PARM_GENERIC_DIGITS, str_init("Generic Digits"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_EGRESS_SERV, str_init("Egress Service"), NULL, NULL, NULL, NULL, 0},
{ISUP_PARM_JIP, str_init("Jurisdiction Information Parameter"), NULL, NULL, NULL, NULL, 0},
Expand Down
2 changes: 1 addition & 1 deletion modules/tm/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ inline static void retransmission_handler( struct timer_link *retr_tl )
/* re-transmission */
if ( r_buf->activ_type==TYPE_LOCAL_CANCEL
|| r_buf->activ_type==TYPE_REQUEST ) {
LM_DBG("retransmission_handler : request resending"
LM_INFO("retransmission_handler : request resending"
" (t=%p, %.9s ... )\n", r_buf->my_T, r_buf->buffer.s);
set_t(r_buf->my_T);
if (SEND_BUFFER( r_buf )==0) {
Expand Down