void rtcp_sdes_parse(const mblk_t *m, SdesItemFoundCallback cb, void *user_data){ uint8_t *rptr=(uint8_t*)m->b_rptr+sizeof(rtcp_common_header_t); const rtcp_common_header_t *ch=(rtcp_common_header_t*)m->b_rptr; uint8_t *end=rptr+sizeof(rtcp_common_header_t)+ (4*rtcp_common_header_get_length(ch)); uint32_t ssrc=0; int nchunk=0; bool_t chunk_start=TRUE; if (end>(uint8_t*)m->b_wptr) end=(uint8_t*)m->b_wptr; while(rptr<end){ if (chunk_start){ if (rptr+4<=end){ ssrc=ntohl(*(uint32_t*)rptr); rptr+=4; }else{ ortp_warning("incorrect chunk start in RTCP SDES"); break; } chunk_start=FALSE; }else{ if (rptr+2<=end){ uint8_t type=rptr[0]; uint8_t len=rptr[1]; if (type==RTCP_SDES_END){ /* pad to next 32bit boundary*/ rptr=(uint8_t*)(((unsigned long)rptr+4) & ~0x3); nchunk++; if (nchunk<rtcp_common_header_get_rc(ch)){ chunk_start=TRUE; continue; }else break; } rptr+=2; if (rptr+len<=end){ cb(user_data,ssrc,type,(char*)rptr,len); rptr+=len; }else{ ortp_warning("bad item length in RTCP SDES"); break; } }else{ /*end of packet */ break; } } } }
/** * Set session's SDES item for automatic sending of RTCP compound packets. * If some items are not specified, use NULL. **/ void rtp_session_set_source_description(RtpSession *session, const char *cname, const char *name, const char *email, const char *phone, const char *loc, const char *tool, const char *note) { mblk_t *m; mblk_t *chunk = sdes_chunk_new(session->snd.ssrc); if (strlen(cname)>255) { /* * rfc3550, * 6.5 SDES: Source Description RTCP Packet * ... * Note that the text can be no longer than 255 octets, * * */ ortp_warning("Cname [%s] too long for session [%p]",cname,session); } sdes_chunk_set_full_items(chunk, cname, name, email, phone, loc, tool, note); if (session->full_sdes != NULL) freemsg(session->full_sdes); session->full_sdes = chunk; chunk = sdes_chunk_new(session->snd.ssrc); m = sdes_chunk_set_minimal_items(chunk, cname); m = sdes_chunk_pad(m); if (session->minimal_sdes != NULL) freemsg(session->minimal_sdes); session->minimal_sdes = chunk; }
static void set_high_prio(){ #ifndef _WIN32 const char *sched_pref=getenv("ORTP_SIMULATOR_SCHED_POLICY"); int policy=SCHED_OTHER; struct sched_param param; int result=0; char* env_prio_c=NULL; int min_prio, max_prio, env_prio; if (sched_pref && strcasecmp(sched_pref,"SCHED_RR")==0){ policy=SCHED_RR; }else if (sched_pref && strcasecmp(sched_pref,"SCHED_FIFO")==0){ policy=SCHED_FIFO; } memset(¶m,0,sizeof(param)); min_prio = sched_get_priority_min(policy); max_prio = sched_get_priority_max(policy); env_prio_c = getenv("ORTP_SIMULATOR_SCHED_PRIO"); env_prio = (env_prio_c == NULL)?max_prio:atoi(env_prio_c); env_prio = MAX(MIN(env_prio, max_prio), min_prio); param.sched_priority=env_prio; if((result=pthread_setschedparam(pthread_self(),policy, ¶m))) { ortp_warning("Ortp simulator: set pthread_setschedparam failed: %s",strerror(result)); } else { ortp_message("ortp network simulator: sched policy set to %s and priority value (%i)", sched_policy_to_string(policy), param.sched_priority); } #endif }
void posix_timer_do() { int diff,time; struct timeval tv; gettimeofday(&cur,NULL); time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 ); if ( (diff=time-posix_timer_time)>50){ ortp_warning("Must catchup %i miliseconds.",diff); } while((diff = posix_timer_time-time) > 0) { tv.tv_sec = diff/1000; tv.tv_usec = (diff%1000)*1000; #if defined(_WIN32) || defined(_WIN32_WCE) /* this kind of select is not supported on windows */ Sleep(tv.tv_usec/1000 + tv.tv_sec * 1000); #else select(0,NULL,NULL,NULL,&tv); #endif gettimeofday(&cur,NULL); time=((cur.tv_usec-orig.tv_usec)/1000 ) + ((cur.tv_sec-orig.tv_sec)*1000 ); } posix_timer_time+=POSIXTIMER_INTERVAL/1000; }
void win_timer_do(void) { DWORD diff; // If timer have expired while we where out of this method // Try to run after lost time. if (late_ticks > 0) { late_ticks--; posix_timer_time+=TIME_INTERVAL; return; } diff = GetTickCount() - posix_timer_time - offset_time; if( diff>TIME_INTERVAL && (diff<(1<<31))) { late_ticks = diff/TIME_INTERVAL; ortp_warning("we must catchup %i ticks.",late_ticks); return; } WaitForSingleObject(TimeEvent,TIME_TIMEOUT); return; }
static bool_t queue_packet(queue_t *q, int maxrqsz, mblk_t *mp, rtp_header_t *rtp, int *discarded, int *duplicate) { mblk_t *tmp; int header_size; *discarded=0; *duplicate=0; header_size=RTP_FIXED_HEADER_SIZE+ (4*rtp->cc); if ((mp->b_wptr - mp->b_rptr)==header_size){ ortp_debug("Rtp packet contains no data."); (*discarded)++; freemsg(mp); return FALSE; } /* and then add the packet to the queue */ if (rtp_putq(q,mp) < 0) { /* It was a duplicate packet */ (*duplicate)++; } /* make some checks: q size must not exceed RtpStream::max_rq_size */ while (q->q_mcount > maxrqsz) { /* remove the oldest mblk_t */ tmp=getq(q); if (mp!=NULL) { ortp_warning("rtp_putq: Queue is full. Discarding message with ts=%u",((rtp_header_t*)mp->b_rptr)->timestamp); freemsg(tmp); (*discarded)++; } } return TRUE; }
static OrtpZrtpContext* ortp_zrtp_configure_context(OrtpZrtpContext *userData, RtpSession *s, OrtpZrtpParams *params) { ZrtpContext *context=userData->zrtpContext; if (s->rtp.tr || s->rtcp.tr) ortp_warning("Overwriting rtp or rtcp transport with ZRTP one"); userData->rtpt.data=context; userData->rtpt.t_getsocket=ozrtp_rtp_getsocket; userData->rtpt.t_sendto=ozrtp_rtp_sendto; userData->rtpt.t_recvfrom=ozrtp_rtp_recvfrom; userData->rtcpt.data=context; userData->rtcpt.t_getsocket=ozrtp_rtcp_getsocket; userData->rtcpt.t_sendto=ozrtp_rtcp_sendto; userData->rtcpt.t_recvfrom=ozrtp_rtcp_recvfrom; rtp_session_set_transports(s, &userData->rtpt, &userData->rtcpt); ortp_message("Starting ZRTP engine"); zrtp_setEnrollmentMode(context,FALSE);//because it is uninitialized in zrtpcpp. zrtp_startZrtpEngine(context); return userData; }
OrtpZrtpContext* ortp_zrtp_multistream_new(OrtpZrtpContext* activeContext, RtpSession *s, OrtpZrtpParams *params) { int32_t length; char *multiparams=NULL; int i=0; if (!zrtp_isMultiStreamAvailable(activeContext->zrtpContext)) { ortp_warning("could't add stream: mutlistream not supported by peer"); } if (zrtp_isMultiStream(activeContext->zrtpContext)) { ortp_fatal("Error: should derive multistream from DH or preshared modes only"); } multiparams=zrtp_getMultiStrParams(activeContext->zrtpContext, &length); ortp_message("ZRTP multiparams length is %d", length); for (;i<length;i++) { ortp_message("%d", multiparams[i]); } ortp_message("Initializing ZRTP context"); ZrtpContext *context = zrtp_CreateWrapper(); OrtpZrtpContext *userData=createUserData(context); userData->session=s; ortp_zrtp_configure(context); zrtp_initializeZrtpEngine(context, &userData->zrtp_cb, userAgentStr, params->zid_file, userData, 0); ortp_message("setting zrtp_setMultiStrParams"); zrtp_setMultiStrParams(context,multiparams,length); return ortp_zrtp_configure_context(userData,s,params); }
/*APP accessors */ bool_t rtcp_is_APP(const mblk_t *m){ const rtcp_common_header_t *ch=rtcp_get_common_header(m); if (ch!=NULL && rtcp_common_header_get_packet_type(ch)==RTCP_APP){ if (msgdsize(m)<sizeof(rtcp_common_header_t)+ rtcp_common_header_get_length(ch)){ ortp_warning("Too short RTCP APP packet."); return FALSE; } if (sizeof(rtcp_common_header_t)+rtcp_common_header_get_length(ch) < sizeof(rtcp_app_t)){ ortp_warning("Bad RTCP APP packet."); return FALSE; } return TRUE; } return FALSE; }
void rtp_timer_set_interval(RtpTimer *timer, struct timeval *interval) { if (timer->state==RTP_TIMER_RUNNING){ ortp_warning("Cannot change timer interval while it is running.\n"); return; } timer->interval.tv_sec=interval->tv_sec; timer->interval.tv_usec=interval->tv_usec; }
void rtp_scheduler_stop(RtpScheduler *sched) { if (sched->thread_running==1) { sched->thread_running=0; ortp_thread_join(sched->thread, NULL); } else ortp_warning("Scheduler thread is not running."); }
void rtp_scheduler_set_timer(RtpScheduler *sched,RtpTimer *timer) { if (sched->thread_running){ ortp_warning("Cannot change timer while the scheduler is running !!"); return; } sched->timer=timer; /* report the timer increment */ sched->timer_inc=(timer->interval.tv_usec/1000) + (timer->interval.tv_sec*1000); }
/*Receiver report accessors*/ bool_t rtcp_is_RR(const mblk_t *m){ const rtcp_common_header_t *ch=rtcp_get_common_header(m); if (ch!=NULL && rtcp_common_header_get_packet_type(ch)==RTCP_RR){ if (msgdsize(m)<sizeof(rtcp_rr_t)){ ortp_warning("Too short RTCP RR packet."); return FALSE; } return TRUE; } return FALSE; }
void rtp_scheduler_start(RtpScheduler *sched) { if (sched->thread_running==0){ sched->thread_running=1; ortp_mutex_lock(&sched->lock); ortp_thread_create(&sched->thread, NULL, rtp_scheduler_schedule,(void*)sched); ortp_cond_wait(&sched->unblock_select_cond,&sched->lock); ortp_mutex_unlock(&sched->lock); } else ortp_warning("Scheduler thread already running."); }
/*BYE accessors */ bool_t rtcp_is_BYE(const mblk_t *m){ const rtcp_common_header_t *ch=rtcp_get_common_header(m); if (ch && rtcp_common_header_get_packet_type(ch)==RTCP_BYE){ if (msgdsize(m)<sizeof(rtcp_common_header_t)+ rtcp_common_header_get_length(ch)){ ortp_warning("Too short RTCP BYE packet."); return FALSE; } return TRUE; } return FALSE; }
const report_block_t * rtcp_SR_get_report_block(const mblk_t *m, int idx){ rtcp_sr_t *sr=(rtcp_sr_t*)m->b_rptr; report_block_t *rb=&sr->rb[idx]; int size=sizeof(rtcp_common_header_t)+(4*rtcp_common_header_get_length(&sr->ch)); if ( ( (uint8_t*)rb)+sizeof(report_block_t) <= m->b_rptr + size ) { return rb; }else{ if (idx<rtcp_common_header_get_rc(&sr->ch)){ ortp_warning("RTCP packet should include a report_block_t at pos %i but has no space for it.",idx); } } return NULL; }
/* get common header; this function will also check the sanity of the packet*/ const rtcp_common_header_t * rtcp_get_common_header(const mblk_t *m){ int size=msgdsize(m); rtcp_common_header_t *ch; if (m->b_cont!=NULL){ ortp_fatal("RTCP parser does not work on fragmented mblk_t. Use msgpullup() before to re-assemble the packet."); return NULL; } if (size<sizeof(rtcp_common_header_t)){ ortp_warning("Bad RTCP packet, too short."); return NULL; } ch=(rtcp_common_header_t*)m->b_rptr; return ch; }
bool_t rtcp_BYE_get_ssrc(const mblk_t *m, int idx, uint32_t *ssrc){ rtcp_bye_t *bye=(rtcp_bye_t*)m->b_rptr; int rc=rtcp_common_header_get_rc(&bye->ch); int len=rtcp_common_header_get_length(&bye->ch); if (idx<rc){ if ((uint8_t*)&bye->ssrc[idx]<=(m->b_rptr +sizeof(rtcp_common_header_t)+len-4)) { *ssrc=ntohl(bye->ssrc[idx]); return TRUE; }else{ ortp_warning("RTCP BYE should contain %i ssrc, but there is not enough room for it.",rc); } } return FALSE; }
/** * Gracefully uninitialize the library, including shutdowning the scheduler if it was started. * **/ void ortp_exit() { if (ortp_initialized==0) { ortp_warning("ortp_exit() called without prior call to ortp_init(), ignored."); return; } ortp_initialized--; if (ortp_initialized==0){ if (__ortp_scheduler!=NULL) { rtp_scheduler_destroy(__ortp_scheduler); __ortp_scheduler=NULL; } ortp_uninit_logger(); } }
bool_t ortp_loss_rate_estimator_process_report_block(OrtpLossRateEstimator *obj, const RtpSession *session, const report_block_t *rb){ int32_t cum_loss=report_block_get_cum_packet_lost(rb); int32_t extseq=report_block_get_high_ext_seq(rb); int32_t diff_unique_outgoing=(int32_t)(session->stats.packet_sent-obj->last_packet_sent_count); int32_t diff_total_outgoing=diff_unique_outgoing+(int32_t)(session->stats.packet_dup_sent-obj->last_dup_packet_sent_count); int32_t diff; uint64_t curtime; bool_t got_value=FALSE; if (obj->last_ext_seq==-1 || obj->last_estimate_time_ms==(uint64_t)-1){ /*first report cannot be considered, since we don't know the interval it covers*/ obj->last_ext_seq=extseq; obj->last_cum_loss=cum_loss; obj->last_estimate_time_ms=ortp_get_cur_time_ms(); return FALSE; } diff=extseq-obj->last_ext_seq; curtime=ortp_get_cur_time_ms(); if (diff<0 || diff>obj->min_packet_count_interval * 100){ ortp_warning("ortp_loss_rate_estimator_process %p: Suspected discontinuity in sequence numbering from %d to %d.", obj, obj->last_ext_seq, extseq); obj->last_ext_seq=extseq; obj->last_cum_loss=cum_loss; obj->last_packet_sent_count=session->stats.packet_sent; obj->last_dup_packet_sent_count=session->stats.packet_dup_sent; }else if (diff>obj->min_packet_count_interval && curtime-obj->last_estimate_time_ms>=obj->min_time_ms_interval){ /*we have sufficient interval*/ int32_t new_losses=cum_loss-obj->last_cum_loss; /*if we are using duplicates, they will not be visible in 'diff' variable. But since we are the emitter, we can retrieve the total count of packet we sent and use this value to compute the loss rate instead.*/ obj->loss_rate = 100.f * (1.f - MAX(0, (diff_unique_outgoing - new_losses) * 1.f / diff_total_outgoing)); /*update last values with current*/ got_value=TRUE; obj->last_estimate_time_ms=curtime; if (obj->loss_rate>100.f){ ortp_error("ortp_loss_rate_estimator_process %p: Loss rate MUST NOT be greater than 100%%", obj); } obj->last_ext_seq=extseq; obj->last_cum_loss=cum_loss; obj->last_packet_sent_count=session->stats.packet_sent; obj->last_dup_packet_sent_count=session->stats.packet_dup_sent; } return got_value; }
/** * Gracefully uninitialize the library, including shutdowning the scheduler if it was started. * **/ void ortp_exit() { if (ortp_initialized==0) { ortp_warning("ortp_exit() called without prior call to ortp_init(), ignored."); return; } ortp_initialized--; if (ortp_initialized==0){ if (__ortp_scheduler!=NULL) { rtp_scheduler_destroy(__ortp_scheduler); __ortp_scheduler=NULL; } #ifdef HAVE_SRTP ortp_srtp_shutdown(); #endif } }
bool_t rtcp_BYE_get_reason(const mblk_t *m, const char **reason, int *reason_len){ rtcp_bye_t *bye=(rtcp_bye_t*)m->b_rptr; int rc=rtcp_common_header_get_rc(&bye->ch); int len=rtcp_common_header_get_length(&bye->ch); uint8_t *rptr=(uint8_t*)m->b_rptr+sizeof(rtcp_common_header_t)+rc*4; uint8_t *end=(uint8_t*)(m->b_rptr+sizeof(rtcp_common_header_t)+len); if (rptr<end){ uint8_t content_len=rptr[0]; if (rptr+1+content_len<=end){ *reason=(char*)rptr+1; *reason_len=content_len; return TRUE; }else{ ortp_warning("RTCP BYE has not enough space for reason phrase."); return FALSE; } } return FALSE; }
/** * Allocates a new rtp packet to be used to add named telephony events. The application can use * then rtp_session_add_telephone_event() to add named events to the packet. * Finally the packet has to be sent with rtp_session_sendm_with_ts(). * * @param session a rtp session. * @param start boolean to indicate if the marker bit should be set. * * @return a message block containing the rtp packet if successfull, NULL if the rtp session * cannot support telephony event (because the rtp profile it is bound to does not include * a telephony event payload type). **/ mblk_t *rtp_session_create_telephone_event_packet(RtpSession *session, int start) { mblk_t *mp; rtp_header_t *rtp; PayloadType *cur_pt=rtp_profile_get_payload(session->snd.profile, rtp_session_get_send_payload_type(session)); int tev_pt = session->tev_send_pt; if (tev_pt != -1){ PayloadType *cur_tev_pt=rtp_profile_get_payload(session->snd.profile, tev_pt); if (!cur_tev_pt){ ortp_error("Undefined telephone-event payload type %i choosen for sending telephone event", tev_pt); tev_pt = -1; }else if (cur_pt && cur_tev_pt->clock_rate != cur_pt->clock_rate){ ortp_warning("Telephone-event payload type %i has clockrate %i while main audio codec has clockrate %i: this is not permitted.", tev_pt, cur_tev_pt->clock_rate, cur_pt->clock_rate); } } if (tev_pt == -1){ tev_pt = rtp_profile_find_payload_number(session->snd.profile, "telephone-event", cur_pt ? cur_pt->clock_rate : 8000, 1); } return_val_if_fail(tev_pt!=-1,NULL); mp=allocb(RTP_FIXED_HEADER_SIZE+TELEPHONY_EVENTS_ALLOCATED_SIZE,BPRI_MED); if (mp==NULL) return NULL; rtp=(rtp_header_t*)mp->b_rptr; rtp->version = 2; rtp->markbit=start; rtp->padbit = 0; rtp->extbit = 0; rtp->cc = 0; rtp->ssrc = session->snd.ssrc; /* timestamp set later, when packet is sended */ /*seq number set later, when packet is sended */ /*set the payload type */ rtp->paytype=tev_pt; /*copy the payload */ mp->b_wptr+=RTP_FIXED_HEADER_SIZE; return mp; }
void rtp_scheduler_remove_session(RtpScheduler *sched, RtpSession *session) { RtpSession *tmp; int cond=1; return_if_fail(session!=NULL); if (!(session->flags & RTP_SESSION_IN_SCHEDULER)){ /* the rtp session is not scheduled, so return silently */ return; } rtp_scheduler_lock(sched); tmp=sched->list; if (tmp==session){ sched->list=tmp->next; rtp_session_unset_flag(session,RTP_SESSION_IN_SCHEDULER); session_set_clr(&sched->all_sessions,session); rtp_scheduler_unlock(sched); return; } /* go the position of session in the list */ while(cond){ if (tmp!=NULL){ if (tmp->next==session){ tmp->next=tmp->next->next; cond=0; } else tmp=tmp->next; }else { /* the session was not found ! */ ortp_warning("rtp_scheduler_remove_session: the session was not found in the scheduler list!"); cond=0; } } rtp_session_unset_flag(session,RTP_SESSION_IN_SCHEDULER); /* delete the bit in the mask */ session_set_clr(&sched->all_sessions,session); rtp_scheduler_unlock(sched); }
/** * A variation of rtp_session_send_dtmf() with duration specified. * * @param session a rtp session * @param dtmf a character meaning the dtmf (ex: '1', '#' , '9' ...) * @param userts the timestamp * @param duration duration of the dtmf in timestamp units * @return 0 if successfull, -1 if the session cannot support telephony events or if the dtmf given as argument is not valid. **/ int rtp_session_send_dtmf2(RtpSession *session, char dtmf, uint32_t userts, int duration) { mblk_t *m1,*m2,*m3; int tev_type; int durationtier = duration/3; /* create the first telephony event packet */ switch (dtmf){ case '1': tev_type=TEV_DTMF_1; break; case '2': tev_type=TEV_DTMF_2; break; case '3': tev_type=TEV_DTMF_3; break; case '4': tev_type=TEV_DTMF_4; break; case '5': tev_type=TEV_DTMF_5; break; case '6': tev_type=TEV_DTMF_6; break; case '7': tev_type=TEV_DTMF_7; break; case '8': tev_type=TEV_DTMF_8; break; case '9': tev_type=TEV_DTMF_9; break; case '*': tev_type=TEV_DTMF_STAR; break; case '0': tev_type=TEV_DTMF_0; break; case '#': tev_type=TEV_DTMF_POUND; break; case 'A': case 'a': tev_type=TEV_DTMF_A; break; case 'B': case 'b': tev_type=TEV_DTMF_B; break; case 'C': case 'c': tev_type=TEV_DTMF_C; break; case 'D': case 'd': tev_type=TEV_DTMF_D; break; case '!': tev_type=TEV_FLASH; break; default: ortp_warning("Bad dtmf: %c.",dtmf); return -1; } m1=rtp_session_create_telephone_event_packet(session,1); if (m1==NULL) return -1; rtp_session_add_telephone_event(session,m1,tev_type,0,10,durationtier); /* create a second packet */ m2=rtp_session_create_telephone_event_packet(session,0); if (m2==NULL) return -1; rtp_session_add_telephone_event(session,m2,tev_type,0,10, durationtier+durationtier); /* create a third and final packet */ m3=rtp_session_create_telephone_event_packet(session,0); if (m3==NULL) return -1; rtp_session_add_telephone_event(session,m3,tev_type,1,10,duration); /* and now sends them */ rtp_session_sendm_with_ts(session,m1,userts); rtp_session_sendm_with_ts(session,m2,userts); /* the last packet is sent three times in order to improve reliability*/ m1=copymsg(m3); m2=copymsg(m3); /* NOTE: */ /* we need to copymsg() instead of dupmsg() because the buffers are modified when the packet is sended because of the host-to-network conversion of timestamp,ssrc, csrc, and seq number. */ rtp_session_sendm_with_ts(session,m3,userts); session->rtp.snd_seq--; rtp_session_sendm_with_ts(session,m1,userts); session->rtp.snd_seq--; rtp_session_sendm_with_ts(session,m2,userts); return 0; }
void rtp_session_rtp_parse(RtpSession *session, mblk_t *mp, uint32_t local_str_ts, struct sockaddr *addr, socklen_t addrlen) { int i; rtp_header_t *rtp; int msgsize; RtpStream *rtpstream=&session->rtp; rtp_stats_t *stats=&rtpstream->stats; return_if_fail(mp!=NULL); msgsize=msgdsize(mp); if (msgsize<RTP_FIXED_HEADER_SIZE){ ortp_warning("Packet too small to be a rtp packet (%i)!",msgsize); rtpstream->stats.bad++; ortp_global_stats.bad++; freemsg(mp); return; } rtp=(rtp_header_t*)mp->b_rptr; if (rtp->version!=2) { /* try to see if it is a STUN packet */ uint16_t stunlen=*((uint16_t*)(mp->b_rptr + sizeof(uint16_t))); stunlen = ntohs(stunlen); if (stunlen+20==mp->b_wptr-mp->b_rptr){ /* this looks like a stun packet */ if (session->eventqs!=NULL){ OrtpEvent *ev=ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED); OrtpEventData *ed=ortp_event_get_data(ev); ed->packet=mp; ed->ep=rtp_endpoint_new(addr,addrlen); rtp_session_dispatch_event(session,ev); return; } } freemsg(mp); return; } /* only count non-stun packets. */ ortp_global_stats.packet_recv++; stats->packet_recv++; ortp_global_stats.hw_recv+=msgsize; stats->hw_recv+=msgsize; session->rtp.hwrcv_since_last_SR++; if (rtp->version!=2) { /* discard*/ ortp_debug("Receiving rtp packet with version number !=2...discarded"); stats->bad++; ortp_global_stats.bad++; freemsg(mp); return; } /* convert all header data from network order to host order */ rtp->seq_number=ntohs(rtp->seq_number); rtp->timestamp=ntohl(rtp->timestamp); rtp->ssrc=ntohl(rtp->ssrc); /* convert csrc if necessary */ if (rtp->cc*sizeof(uint32_t) > (uint32_t) (msgsize-RTP_FIXED_HEADER_SIZE)){ ortp_debug("Receiving too short rtp packet."); stats->bad++; ortp_global_stats.bad++; freemsg(mp); return; } /* Write down the last RTP/RTCP packet reception time. */ gettimeofday(&session->last_recv_time, NULL); for (i=0;i<rtp->cc;i++) rtp->csrc[i]=ntohl(rtp->csrc[i]); if (session->rcv.ssrc!=0) { /*the ssrc is set, so we must check it */ if (session->rcv.ssrc!=rtp->ssrc){ /*ortp_debug("rtp_parse: bad ssrc - %i",rtp->ssrc);*/ session->rcv.ssrc=rtp->ssrc; rtp_signal_table_emit(&session->on_ssrc_changed); } }else session->rcv.ssrc=rtp->ssrc; /* update some statistics */ { poly32_t *extseq=(poly32_t*)&rtpstream->hwrcv_extseq; if (rtp->seq_number>extseq->split.lo){ extseq->split.lo=rtp->seq_number; }else if (rtp->seq_number<200 && extseq->split.lo>((1<<16) - 200)){ /* this is a check for sequence number looping */ extseq->split.lo=rtp->seq_number; extseq->split.hi++; } } /* check for possible telephone events */ if (rtp->paytype==session->rcv.telephone_events_pt){ split_and_queue(&session->rtp.tev_rq,session->rtp.max_rq_size,mp,rtp,&i); stats->discarded+=i; ortp_global_stats.discarded+=i; return; } /* check for possible payload type change, in order to update accordingly our clock-rate dependant parameters */ if (session->hw_recv_pt!=rtp->paytype){ rtp_session_update_payload_type(session,rtp->paytype); } if (session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED) { int32_t slide=0; int32_t safe_delay=0; jitter_control_new_packet(&session->rtp.jittctl,rtp->timestamp,local_str_ts,&slide,&safe_delay); session->rtp.rcv_diff_ts=session->rtp.hwrcv_diff_ts + slide - safe_delay; ortp_debug(" rcv_diff_ts=%i", session->rtp.rcv_diff_ts); /* detect timestamp important jumps in the future, to workaround stupid rtp senders */ if (RTP_TIMESTAMP_IS_NEWER_THAN(rtp->timestamp,session->rtp.rcv_last_ts+session->rtp.ts_jump)){ ortp_debug("rtp_parse: timestamp jump ?"); rtp_signal_table_emit2(&session->on_timestamp_jump,(long)&rtp->timestamp); } else if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts,rtp->timestamp)){ /* don't queue packets older than the last returned packet to the application*/ /* Call timstamp jumb in case of * large negative Ts jump or if ts is set to 0 */ if ( RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts, rtp->timestamp + session->rtp.ts_jump) ){ ortp_warning("rtp_parse: negative timestamp jump"); rtp_signal_table_emit2(&session->on_timestamp_jump, (long)&rtp->timestamp); } ortp_debug("rtp_parse: discarding too old packet (ts=%i)",rtp->timestamp); freemsg(mp); stats->outoftime++; ortp_global_stats.outoftime++; return; } } split_and_queue(&session->rtp.rq,session->rtp.max_rq_size,mp,rtp,&i); stats->discarded+=i; ortp_global_stats.discarded+=i; }
/** * Send information messages to the hosting environment. * * The ZRTP implementation uses this method to send information * messages to the host. Along with the message ZRTP provides a * severity indicator that defines: Info, Warning, Error, * Alert. Refer to the <code>MessageSeverity</code> enum above. * * @param ctx * Pointer to the opaque ZrtpContext structure. * @param severity * This defines the message's severity * @param subCode * The subcode identifying the reason. * @see ZrtpCodes#MessageSeverity */ static void ozrtp_sendInfo (ZrtpContext* ctx, int32_t severity, int32_t subCode ) { const char* submsg; switch (subCode) { case zrtp_InfoHelloReceived: /*!< Hello received, preparing a Commit */ submsg="zrtp_InfoHelloReceived"; break; case zrtp_InfoCommitDHGenerated: /*!< Commit: Generated a public DH key */ submsg="zrtp_InfoCommitDHGenerated"; break; case zrtp_InfoRespCommitReceived: /*!< Responder: Commit received, preparing DHPart1 */ submsg="zrtp_InfoRespCommitReceived"; break; case zrtp_InfoDH1DHGenerated: /*!< DH1Part: Generated a public DH key */ submsg="zrtp_InfoDH1DHGenerated"; break; case zrtp_InfoInitDH1Received: /*!< Initiator: DHPart1 received, preparing DHPart2 */ submsg="zrtp_InfoInitDH1Received"; break; case zrtp_InfoRespDH2Received: /*!< Responder: DHPart2 received, preparing Confirm1 */ submsg="zrtp_InfoRespDH2Received"; break; case zrtp_InfoInitConf1Received: /*!< Initiator: Confirm1 received, preparing Confirm2 */ submsg="zrtp_InfoInitConf1Received"; break; case zrtp_InfoRespConf2Received: /*!< Responder: Confirm2 received, preparing Conf2Ack */ submsg="zrtp_InfoRespConf2Received"; break; case zrtp_InfoRSMatchFound: /*!< At least one retained secrets matches - security OK */ submsg="zrtp_InfoRSMatchFound"; break; case zrtp_InfoSecureStateOn: /*!< Entered secure state */ submsg="zrtp_InfoSecureStateOn"; break; case zrtp_InfoSecureStateOff: /*!< No more security for this session */ submsg="zrtp_InfoSecureStateOff"; break; default: submsg="unkwown"; break; } switch (severity) { case zrtp_Info: ortp_message("ZRTP INFO %s",submsg); break; case zrtp_Warning: /*!< A Warning message - security can be established */ ortp_warning("ZRTP %s",submsg); break; case zrtp_Severe:/*!< Severe error, security will not be established */ ortp_error("ZRTP SEVERE %s",submsg); break; case zrtp_ZrtpError: ortp_error("ZRTP ERROR %s",submsg); break; default: ortp_error("ZRTP UNKNOWN ERROR %s",submsg); break; } if (subCode == zrtp_InfoSecureStateOn || subCode == zrtp_InfoSecureStateOff) { OrtpEventData *eventData; OrtpEvent *ev; ev=ortp_event_new(ORTP_EVENT_ZRTP_ENCRYPTION_CHANGED); eventData=ortp_event_get_data(ev); eventData->info.zrtp_stream_encrypted=(subCode == zrtp_InfoSecureStateOn); rtp_session_dispatch_event(user_data(ctx)->session, ev); } }
void rtp_session_rtp_parse(RtpSession *session, mblk_t *mp, uint32_t local_str_ts, struct sockaddr *addr, socklen_t addrlen) { int i; int discarded; int duplicate; rtp_header_t *rtp; int msgsize; RtpStream *rtpstream=&session->rtp; rtp_stats_t *stats=&rtpstream->stats; msgsize=(int)(mp->b_wptr-mp->b_rptr); if (msgsize<RTP_FIXED_HEADER_SIZE){ ortp_warning("Packet too small to be a rtp packet (%i)!",msgsize); rtpstream->stats.bad++; ortp_global_stats.bad++; freemsg(mp); return; } rtp=(rtp_header_t*)mp->b_rptr; if (rtp->version!=2) { /* try to see if it is a STUN packet */ uint16_t stunlen=*((uint16_t*)(mp->b_rptr + sizeof(uint16_t))); stunlen = ntohs(stunlen); if (stunlen+20==mp->b_wptr-mp->b_rptr){ /* this looks like a stun packet */ if (session->eventqs!=NULL){ OrtpEvent *ev=ortp_event_new(ORTP_EVENT_STUN_PACKET_RECEIVED); OrtpEventData *ed=ortp_event_get_data(ev); ed->packet=mp; memcpy(&ed->source_addr,addr,addrlen); ed->source_addrlen=addrlen; ed->info.socket_type = OrtpRTPSocket; rtp_session_dispatch_event(session,ev); return; } } /* discard in two case: the packet is not stun OR nobody is interested by STUN (no eventqs) */ ortp_debug("Receiving rtp packet with version number !=2...discarded"); stats->bad++; ortp_global_stats.bad++; freemsg(mp); return; } /* only count non-stun packets. */ ortp_global_stats.packet_recv++; stats->packet_recv++; ortp_global_stats.hw_recv+=msgsize; stats->hw_recv+=msgsize; session->rtp.hwrcv_since_last_SR++; session->rtcp_xr_stats.rcv_since_last_stat_summary++; /* convert all header data from network order to host order */ rtp->seq_number=ntohs(rtp->seq_number); rtp->timestamp=ntohl(rtp->timestamp); rtp->ssrc=ntohl(rtp->ssrc); /* convert csrc if necessary */ if (rtp->cc*sizeof(uint32_t) > (uint32_t) (msgsize-RTP_FIXED_HEADER_SIZE)){ ortp_debug("Receiving too short rtp packet."); stats->bad++; ortp_global_stats.bad++; freemsg(mp); return; } #ifndef PERF /* Write down the last RTP/RTCP packet reception time. */ ortp_gettimeofday(&session->last_recv_time, NULL); #endif for (i=0;i<rtp->cc;i++) rtp->csrc[i]=ntohl(rtp->csrc[i]); /*the goal of the following code is to lock on an incoming SSRC to avoid receiving "mixed streams"*/ if (session->ssrc_set){ /*the ssrc is set, so we must check it */ if (session->rcv.ssrc!=rtp->ssrc){ if (session->inc_ssrc_candidate==rtp->ssrc){ session->inc_same_ssrc_count++; }else{ session->inc_same_ssrc_count=0; session->inc_ssrc_candidate=rtp->ssrc; } if (session->inc_same_ssrc_count>=session->rtp.ssrc_changed_thres){ /* store the sender rtp address to do symmetric RTP */ if (!session->use_connect){ if (session->rtp.gs.socket>0 && session->symmetric_rtp){ /* store the sender rtp address to do symmetric RTP */ memcpy(&session->rtp.gs.rem_addr,addr,addrlen); session->rtp.gs.rem_addrlen=addrlen; } } session->rtp.rcv_last_ts = rtp->timestamp; session->rcv.ssrc=rtp->ssrc; rtp_signal_table_emit(&session->on_ssrc_changed); }else{ /*discard the packet*/ ortp_debug("Receiving packet with unknown ssrc."); stats->bad++; ortp_global_stats.bad++; freemsg(mp); return; } } else{ /* The SSRC change must not happen if we still receive ssrc from the initial source. */ session->inc_same_ssrc_count=0; } }else{ session->ssrc_set=TRUE; session->rcv.ssrc=rtp->ssrc; if (!session->use_connect){ if (session->rtp.gs.socket>0 && session->symmetric_rtp){ /* store the sender rtp address to do symmetric RTP */ memcpy(&session->rtp.gs.rem_addr,addr,addrlen); session->rtp.gs.rem_addrlen=addrlen; } } } /* update some statistics */ { poly32_t *extseq=(poly32_t*)&rtpstream->hwrcv_extseq; if (rtp->seq_number>extseq->split.lo){ extseq->split.lo=rtp->seq_number; }else if (rtp->seq_number<200 && extseq->split.lo>((1<<16) - 200)){ /* this is a check for sequence number looping */ extseq->split.lo=rtp->seq_number; extseq->split.hi++; } /* the first sequence number received should be initialized at the beginning or at any resync, so that the first receiver reports contains valid loss rate*/ if (!(session->flags & RTP_SESSION_RECV_SEQ_INIT)){ rtp_session_set_flag(session, RTP_SESSION_RECV_SEQ_INIT); rtpstream->hwrcv_seq_at_last_SR=rtp->seq_number-1; session->rtcp_xr_stats.rcv_seq_at_last_stat_summary=rtp->seq_number-1; } if (stats->packet_recv==1){ session->rtcp_xr_stats.first_rcv_seq=extseq->one; } session->rtcp_xr_stats.last_rcv_seq=extseq->one; } /* check for possible telephone events */ if (rtp_profile_is_telephone_event(session->snd.profile, rtp->paytype)){ queue_packet(&session->rtp.tev_rq,session->rtp.max_rq_size,mp,rtp,&discarded,&duplicate); stats->discarded+=discarded; ortp_global_stats.discarded+=discarded; stats->packet_dup_recv+=duplicate; ortp_global_stats.packet_dup_recv+=duplicate; session->rtcp_xr_stats.discarded_count += discarded; session->rtcp_xr_stats.dup_since_last_stat_summary += duplicate; return; } /* check for possible payload type change, in order to update accordingly our clock-rate dependant parameters */ if (session->hw_recv_pt!=rtp->paytype){ rtp_session_update_payload_type(session,rtp->paytype); } /* Drop the packets while the RTP_SESSION_FLUSH flag is set. */ if (session->flags & RTP_SESSION_FLUSH) { freemsg(mp); return; } jitter_control_new_packet(&session->rtp.jittctl,rtp->timestamp,local_str_ts); update_rtcp_xr_stat_summary(session, mp, local_str_ts); if (session->flags & RTP_SESSION_FIRST_PACKET_DELIVERED) { /* detect timestamp important jumps in the future, to workaround stupid rtp senders */ if (RTP_TIMESTAMP_IS_NEWER_THAN(rtp->timestamp,session->rtp.rcv_last_ts+session->rtp.ts_jump)){ ortp_warning("rtp_parse: timestamp jump in the future detected."); rtp_signal_table_emit2(&session->on_timestamp_jump,&rtp->timestamp); } else if (RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts,rtp->timestamp) || RTP_SEQ_IS_STRICTLY_GREATER_THAN(session->rtp.rcv_last_seq,rtp->seq_number)){ /* don't queue packets older than the last returned packet to the application, or whose sequence number is behind the last packet returned to the application*/ /* Call timstamp jumb in case of * large negative Ts jump or if ts is set to 0 */ if ( RTP_TIMESTAMP_IS_STRICTLY_NEWER_THAN(session->rtp.rcv_last_ts, rtp->timestamp + session->rtp.ts_jump) ){ ortp_warning("rtp_parse: negative timestamp jump detected"); rtp_signal_table_emit2(&session->on_timestamp_jump, &rtp->timestamp); } ortp_debug("rtp_parse: discarding too old packet (ts=%i)",rtp->timestamp); freemsg(mp); stats->outoftime++; ortp_global_stats.outoftime++; session->rtcp_xr_stats.discarded_count++; return; } } if (queue_packet(&session->rtp.rq,session->rtp.max_rq_size,mp,rtp,&discarded,&duplicate)) jitter_control_update_size(&session->rtp.jittctl,&session->rtp.rq); stats->discarded+=discarded; ortp_global_stats.discarded+=discarded; stats->packet_dup_recv+=duplicate; ortp_global_stats.packet_dup_recv+=duplicate; session->rtcp_xr_stats.discarded_count += discarded; session->rtcp_xr_stats.dup_since_last_stat_summary += duplicate; if ((discarded == 0) && (duplicate == 0)) { session->rtcp_xr_stats.rcv_count++; } }
static int ozrtp_rtp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ int rlen; ZrtpContext *zrtpContext = (ZrtpContext*) t->data; OrtpZrtpContext *userData = (OrtpZrtpContext*) zrtpContext->userData; // Do extra stuff first check_timer(zrtpContext, userData); // Check if something to receive rlen=rtp_session_rtp_recv_abstract(t->session->rtp.socket,m,flags,from,fromlen); if (rlen<=0) { // nothing was received or error: pass the information to caller return rlen; } uint8_t* rtp = m->b_rptr; int rtpVersion = ((rtp_header_t*)rtp)->version; // If plain or secured RTP if (rtpVersion == 2) { if (userData->srtpRecv != NULL && zrtp_inState(zrtpContext, SecureState)) { // probably srtp packet, unprotect err_status_t err = srtp_unprotect(userData->srtpRecv,m->b_wptr,&rlen); if (err != err_status_ok) { ortp_warning("srtp_unprotect failed; packet may be plain RTP"); } } // in both cases (RTP plain and deciphered srtp) return rlen; } // if ZRTP packet, send to engine uint32_t *magicField=(uint32_t *)(rtp + 4); if (rlen >= ZRTP_MIN_MSG_LENGTH && rtpVersion==0 && ntohl(*magicField) == ZRTP_MAGIC) { print_zrtp_packet("received", rtp); uint8_t *ext_header = rtp+ZRTP_MESSAGE_OFFSET; uint16_t ext_length = get_zrtp_message_length(ext_header); char messageType[9]; parseZrtpMessageType(messageType, ext_header); // Check max length if (rlen < 12 + ext_length + 4) { ortp_warning("Received malformed ZRTP-like packet: size %d (expected %d)", rlen, 12 + ext_length + 4); return 0; } // Check sequence number uint16_t seq_number = get_rtp_seqnumber(rtp); if (userData->last_recv_zrtp_seq_number != 0 && seq_number <= userData->last_recv_zrtp_seq_number) { // Discard out of order ZRTP packet ortp_message("Discarding received out of order zrtp packet: %d (expected >%d)", seq_number, userData->last_recv_zrtp_seq_number); return 0; } // Check packet checksum uint32_t rcv_crc = get_zrtp_packet_crc((uint32_t*)rtp, ext_length); uint32_t zrtp_total_packet_length = ZRTP_MESSAGE_OFFSET + 4*ext_length + 4; if (!zrtp_CheckCksum(rtp, zrtp_total_packet_length-CRC_SIZE, rcv_crc)) { ortp_warning("Bad ZRTP packet checksum %u total %u", rcv_crc, zrtp_total_packet_length); return 0; } uint32_t peerssrc = ntohl(*(uint32_t*)(rtp+8)); #if HAVE_zrtpcpp_with_len zrtp_processZrtpMessage(zrtpContext, ext_header, peerssrc,rlen); #else zrtp_processZrtpMessage(zrtpContext, ext_header, peerssrc); #endif userData->last_recv_zrtp_seq_number=seq_number; return 0; } else { // Not a ZRTP packet, accept it return rlen; } }