diff --git a/include/actions.hpp b/include/actions.hpp index fc46067a5..c52a4aeac 100644 --- a/include/actions.hpp +++ b/include/actions.hpp @@ -71,6 +71,7 @@ class CAction E_AT_CLOSE_CON, #ifdef PCAPPLAY E_AT_PLAY_PCAP_AUDIO, + E_AT_PLAY_PCAP_IMAGE, E_AT_PLAY_PCAP_VIDEO, #endif #ifdef RTP_STREAM diff --git a/include/call.hpp b/include/call.hpp index 0391f2866..461e5260d 100644 --- a/include/call.hpp +++ b/include/call.hpp @@ -168,6 +168,7 @@ class call : virtual public task, virtual public listener, public virtual socket int hasMediaInformation; pthread_t media_thread; play_args_t play_args_a; + play_args_t play_args_i; play_args_t play_args_v; #endif diff --git a/include/rtpstream.hpp b/include/rtpstream.hpp index 2fcb3bebd..141531e62 100644 --- a/include/rtpstream.hpp +++ b/include/rtpstream.hpp @@ -47,7 +47,8 @@ void rtpstream_shutdown (void); int rtpstream_get_audioport (rtpstream_callinfo_t *callinfo); int rtpstream_get_videoport (rtpstream_callinfo_t *callinfo); -void rtpstream_set_remote (rtpstream_callinfo_t *callinfo, int ip_ver, char *ip_addr, int audio_port, int video_port); +void rtpstream_set_remote (rtpstream_callinfo_t *callinfo, int ip_ver, char *ip_addr, + int audio_port, int video_port); int rtpstream_cache_file (char *filename); void rtpstream_play (rtpstream_callinfo_t *callinfo, rtpstream_actinfo_t *actioninfo); diff --git a/src/actions.cpp b/src/actions.cpp index f58dc46c3..c6296b881 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -147,7 +147,7 @@ void CAction::afficheInfo() } else if (M_action == E_AT_VAR_TO_DOUBLE) { printf("Type[%d] - toDouble varId[%s]", M_action, display_scenario->allocVars->getName(M_varId)); #ifdef PCAPPLAY - } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_VIDEO)) { + } else if ((M_action == E_AT_PLAY_PCAP_AUDIO) || (M_action == E_AT_PLAY_PCAP_IMAGE) || (M_action == E_AT_PLAY_PCAP_VIDEO)) { printf("Type[%d] - file[%s]", M_action, M_pcapArgs->file); #endif @@ -635,7 +635,7 @@ CAction::CAction(scenario *scenario) #endif #ifdef RTP_STREAM - memset (&M_rtpstream_actinfo,0,sizeof(M_rtpstream_actinfo)); + memset(&M_rtpstream_actinfo, 0, sizeof(M_rtpstream_actinfo)); #endif M_scenario = scenario; diff --git a/src/call.cpp b/src/call.cpp index 401cf4077..762d39546 100644 --- a/src/call.cpp +++ b/src/call.cpp @@ -195,12 +195,15 @@ uint8_t get_remote_ipv6_media(char *msg, struct in6_addr *addr) } /* - * Look for "m=audio " or "m=video " pattern in the message and extract the - * following value which should be port number + * Look for "m=audio ", "m=image " or "m=video " pattern in the message + * and extract the following value which should be port number. */ -#define PAT_AUDIO 1 -#define PAT_VIDEO 2 -uint16_t get_remote_port_media(const char *msg, int pattype) +enum media_ptn { + PAT_AUDIO, + PAT_IMAGE, + PAT_VIDEO +}; +uint16_t get_remote_port_media(const char *msg, enum media_ptn pattype) { const char *pattern; char *begin, *end; @@ -208,6 +211,8 @@ uint16_t get_remote_port_media(const char *msg, int pattype) if (pattype == PAT_AUDIO) { pattern = "m=audio "; + } else if (pattype == PAT_IMAGE) { + pattern = "m=image "; } else if (pattype == PAT_VIDEO) { pattern = "m=video "; } else { @@ -244,7 +249,7 @@ uint16_t get_remote_port_media(const char *msg, int pattype) */ void call::get_remote_media_addr(char *msg) { - uint16_t video_port, audio_port; + uint16_t audio_port, image_port, video_port; if (media_ip_is_ipv6) { struct in6_addr ip_media; if (get_remote_ipv6_media(msg, &ip_media)) { @@ -254,16 +259,25 @@ void call::get_remote_media_addr(char *msg) (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_flowinfo = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_scope_id = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_family = AF_INET6; - (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_port = audio_port; + (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_port = htons(audio_port); (_RCAST(struct sockaddr_in6 *, &(play_args_a.to)))->sin6_addr = ip_media; } + image_port = get_remote_port_media(msg, PAT_IMAGE); + if (image_port) { + /* We have image in the SDP: set the to_image addr */ + (_RCAST(struct sockaddr_in6 *, &(play_args_i.to)))->sin6_flowinfo = 0; + (_RCAST(struct sockaddr_in6 *, &(play_args_i.to)))->sin6_scope_id = 0; + (_RCAST(struct sockaddr_in6 *, &(play_args_i.to)))->sin6_family = AF_INET6; + (_RCAST(struct sockaddr_in6 *, &(play_args_i.to)))->sin6_port = htons(image_port); + (_RCAST(struct sockaddr_in6 *, &(play_args_i.to)))->sin6_addr = ip_media; + } video_port = get_remote_port_media(msg, PAT_VIDEO); if (video_port) { /* We have video in the SDP: set the to_video addr */ (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_flowinfo = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_scope_id = 0; (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_family = AF_INET6; - (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_port = video_port; + (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_port = htons(video_port); (_RCAST(struct sockaddr_in6 *, &(play_args_v.to)))->sin6_addr = ip_media; } hasMediaInformation = 1; @@ -276,14 +290,21 @@ void call::get_remote_media_addr(char *msg) if (audio_port) { /* We have audio in the SDP: set the to_audio addr */ (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_family = AF_INET; - (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_port = audio_port; + (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_port = htons(audio_port); (_RCAST(struct sockaddr_in *, &(play_args_a.to)))->sin_addr.s_addr = ip_media; } + image_port = get_remote_port_media(msg, PAT_IMAGE); + if (image_port) { + /* We have image in the SDP: set the to_image addr */ + (_RCAST(struct sockaddr_in *, &(play_args_i.to)))->sin_family = AF_INET; + (_RCAST(struct sockaddr_in *, &(play_args_i.to)))->sin_port = htons(image_port); + (_RCAST(struct sockaddr_in *, &(play_args_i.to)))->sin_addr.s_addr = ip_media; + } video_port = get_remote_port_media(msg, PAT_VIDEO); if (video_port) { /* We have video in the SDP: set the to_video addr */ (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_family = AF_INET; - (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_port = video_port; + (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_port = htons(video_port); (_RCAST(struct sockaddr_in *, &(play_args_v.to)))->sin_addr.s_addr = ip_media; } hasMediaInformation = 1; @@ -299,6 +320,7 @@ void call::get_remote_media_addr(char *msg) #define SDP_IPADDR_PREFIX "\nc=IN IP" #define SDP_AUDIOPORT_PREFIX "\nm=audio" +#define SDP_IMAGEPORT_PREFIX "\nm=image" #define SDP_VIDEOPORT_PREFIX "\nm=video" void call::extract_rtp_remote_addr (char * msg) { @@ -306,8 +328,9 @@ void call::extract_rtp_remote_addr (char * msg) char *copy; char ip_addr[128]; int ip_ver; - int audio_port= 0; - int video_port= 0; + int audio_port = 0; + int image_port = 0; + int video_port = 0; /* Look for start of message body */ search= strstr(msg,"\r\n\r\n"); @@ -349,6 +372,15 @@ void call::extract_rtp_remote_addr (char * msg) } sscanf (search,"%d",&audio_port); } + /* And find the port number for the image stream */ + search= strstr(msg,SDP_IMAGEPORT_PREFIX); + if (search) { + search+= strlen(SDP_IMAGEPORT_PREFIX); + while ( (*search==' ') || (*search=='\t') ) { + search++; + } + sscanf (search,"%d",&image_port); + } /* And find the port number for the video stream */ search= strstr(msg,SDP_VIDEOPORT_PREFIX); if (search) { @@ -358,10 +390,13 @@ void call::extract_rtp_remote_addr (char * msg) } sscanf (search,"%d",&video_port); } - if ((audio_port==0)&&(video_port==0)) { - ERROR("extract_rtp_remote_addr: no m=audio or m=video line found in SDP message body"); + if (audio_port == 0 && image_port == 0 && video_port == 0) { + ERROR("extract_rtp_remote_addr: no m=audio, m=image or m=video line found in SDP message body"); } - rtpstream_set_remote (&rtpstream_callinfo,ip_ver,ip_addr,audio_port,video_port); + /* If we get an image_port only, we won't set anything useful. + * We cannot use rtpstream for udptl/t38 data because it has + * non-linear timing and data size. */ + rtpstream_set_remote(&rtpstream_callinfo, ip_ver, ip_addr, audio_port, video_port); } #endif @@ -612,8 +647,10 @@ void call::init(scenario * call_scenario, struct sipp_socket *socket, struct soc #ifdef PCAPPLAY memset(&(play_args_a.to), 0, sizeof(struct sockaddr_storage)); + memset(&(play_args_i.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.to), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_a.from), 0, sizeof(struct sockaddr_storage)); + memset(&(play_args_i.from), 0, sizeof(struct sockaddr_storage)); memset(&(play_args_v.from), 0, sizeof(struct sockaddr_storage)); hasMediaInformation = 0; media_thread = 0; @@ -2105,9 +2142,8 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf (sockaddr *)(void *)&server_sockaddr, &len); if (server_sockaddr.ss_family == AF_INET6) { - char * temp_dest; - temp_dest = (char *) malloc(INET6_ADDRSTRLEN); - memset(temp_dest,0,INET6_ADDRSTRLEN); + char temp_dest[INET6_ADDRSTRLEN]; /* fits both INET and INET6 */ + temp_dest[0] = temp_dest[INET6_ADDRSTRLEN - 1] = '\0'; inet_ntop(AF_INET6, &((_RCAST(struct sockaddr_in6 *,&server_sockaddr))->sin6_addr), temp_dest, @@ -2139,21 +2175,21 @@ char* call::createSendingMessage(SendingMessage *src, int P_index, char *msg_buf if (begin == msg_buffer) { ERROR("Can not find beginning of a line for the media port!\n"); } + play_args_t *play_args = NULL; if (strstr(begin, "audio")) { - if (media_ip_is_ipv6) { - (_RCAST(struct sockaddr_in6 *, &(play_args_a.from)))->sin6_port = port; - } else { - (_RCAST(struct sockaddr_in *, &(play_args_a.from)))->sin_port = port; - } + play_args = &play_args_a; + } else if (strstr(begin, "image")) { + play_args = &play_args_i; } else if (strstr(begin, "video")) { - if (media_ip_is_ipv6) { - (_RCAST(struct sockaddr_in6 *, &(play_args_v.from)))->sin6_port = port; - } else { - (_RCAST(struct sockaddr_in *, &(play_args_v.from)))->sin_port = port; - } + play_args = &play_args_v; } else { ERROR("media_port keyword with no audio or video on the current line (%s)", begin); } + if (media_ip_is_ipv6) { + (_RCAST(struct sockaddr_in6 *, &(play_args->from)))->sin6_port = htons(port); + } else { + (_RCAST(struct sockaddr_in *, &(play_args->from)))->sin_port = htons(port); + } #endif dest += sprintf(dest, "%u", port); break; @@ -3835,16 +3871,18 @@ call::T_ActionResult call::executeAction(char * msg, message *curmsg) } #ifdef PCAPPLAY } else if ((currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) || + (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) || (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO)) { - play_args_t *play_args = NULL; + play_args_t *play_args; if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_AUDIO) { play_args = &(this->play_args_a); + } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_IMAGE) { + play_args = &(this->play_args_i); } else if (currentAction->getActionType() == CAction::E_AT_PLAY_PCAP_VIDEO) { play_args = &(this->play_args_v); - } - - if (!play_args) + } else { ERROR("Can't find pcap data to play"); + } play_args->pcap = currentAction->getPcapPkts(); /* port number is set in [auto_]media_port interpolation */ @@ -3857,7 +3895,7 @@ call::T_ActionResult call::executeAction(char * msg, message *curmsg) from->sin_family = AF_INET; from->sin_addr.s_addr = inet_addr(media_ip); } - /* Create a thread to send RTP packets */ + /* Create a thread to send RTP or UDPTL packets */ pthread_attr_t attr; pthread_attr_init(&attr); #ifndef PTHREAD_STACK_MIN diff --git a/src/prepare_pcap.c b/src/prepare_pcap.c index cf1898ac7..a525642aa 100644 --- a/src/prepare_pcap.c +++ b/src/prepare_pcap.c @@ -41,11 +41,9 @@ * made available by the platform, as we had no problems to get them on all supported platforms. */ -typedef struct _ether_hdr { - char ether_dst[6]; - char ether_src[6]; +typedef struct _ether_type_hdr { u_int16_t ether_type; /* we only need the type, so we can determine, if the next header is IPv4 or IPv6 */ -} ether_hdr; +} ether_type_hdr; typedef struct _ipv6_hdr { char dontcare[6]; @@ -99,7 +97,9 @@ int prepare_pkts(char *file, pcap_pkts *pkts) u_int16_t base = 0xffff; u_long pktlen; pcap_pkt *pkt_index; - ether_hdr *ethhdr; + size_t ether_type_offset; + ether_type_hdr *ethhdr; + struct iphdr *iphdr; ipv6_hdr *ip6hdr; struct udphdr *udphdr; @@ -110,6 +110,19 @@ int prepare_pkts(char *file, pcap_pkts *pkts) if (!pcap) ERROR("Can't open PCAP file '%s'", file); + switch (pcap_datalink(pcap)) { + case DLT_EN10MB: + /* srcmac[6], dstmac[6], ethertype[2] */ + ether_type_offset = 12; + break; + case DLT_LINUX_SLL: + /* some_stuff[14], ethertype[2] */ + ether_type_offset = 14; + break; + default: + ERROR("Unsupported link-type %d", pcap_datalink(pcap)); + } + #if HAVE_PCAP_NEXT_EX while (pcap_next_ex (pcap, &pkthdr, (const u_char **) &pktdata) == 1) { #else @@ -122,10 +135,14 @@ int prepare_pkts(char *file, pcap_pkts *pkts) ERROR("Can't allocate memory for pcap pkthdr"); while ((pktdata = (u_char *) pcap_next (pcap, pkthdr)) != NULL) { #endif - ethhdr = (ether_hdr *)pktdata; + if (pkthdr->len != pkthdr->caplen) { + ERROR("You got truncated packets. Please create a new dump with -s0"); + } + ethhdr = (ether_type_hdr *)(pktdata + ether_type_offset); if (ntohs(ethhdr->ether_type) != 0x0800 /* IPv4 */ && ntohs(ethhdr->ether_type) != 0x86dd) { /* IPv6 */ - fprintf(stderr, "Ignoring non IP{4,6} packet!\n"); + fprintf(stderr, "Ignoring non IP{4,6} packet, got ether_type %hu!\n", + ntohs(ethhdr->ether_type)); continue; } iphdr = (struct iphdr *)((char *)ethhdr + sizeof(*ethhdr)); diff --git a/src/rtpstream.cpp b/src/rtpstream.cpp index 06cffbbb4..a02ed89c1 100644 --- a/src/rtpstream.cpp +++ b/src/rtpstream.cpp @@ -886,7 +886,8 @@ int rtpstream_get_videoport (rtpstream_callinfo_t *callinfo) } /* code checked */ -void rtpstream_set_remote (rtpstream_callinfo_t *callinfo, int ip_ver, char *ip_addr, int audio_port, int video_port) +void rtpstream_set_remote (rtpstream_callinfo_t *callinfo, int ip_ver, char *ip_addr, + int audio_port, int video_port) { struct sockaddr_storage address; struct in_addr *ip4_addr; @@ -895,7 +896,8 @@ void rtpstream_set_remote (rtpstream_callinfo_t *callinfo, int ip_ver, char *ip_ unsigned count; int nonzero_ip; - debugprint ("rtpstream_set_remote callinfo=%p, ip_ver %d ip_addr %s audio %d video %d\n",callinfo,ip_ver,ip_addr,audio_port,video_port); + debugprint("rtpstream_set_remote callinfo=%p, ip_ver %d ip_addr %s audio %d video %d\n", + callinfo, ip_ver, ip_addr, audio_port, video_port); taskinfo= callinfo->taskinfo; if (!taskinfo) { diff --git a/src/scenario.cpp b/src/scenario.cpp index 83f1bb986..d69ca2135 100644 --- a/src/scenario.cpp +++ b/src/scenario.cpp @@ -1608,6 +1608,9 @@ void scenario::parseAction(CActions *actions) } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) { tmpAction->setPcapArgs(ptr); tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_AUDIO); + } else if ((ptr = xp_get_value((char *) "play_pcap_image"))) { + tmpAction->setPcapArgs(ptr); + tmpAction->setActionType(CAction::E_AT_PLAY_PCAP_IMAGE); hasMedia = 1; } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) { tmpAction->setPcapArgs(ptr); @@ -1616,6 +1619,8 @@ void scenario::parseAction(CActions *actions) #else } else if ((ptr = xp_get_value((char *) "play_pcap_audio"))) { ERROR("Scenario specifies a play_pcap_audio action, but this version of SIPp does not have PCAP support"); + } else if ((ptr = xp_get_value((char *) "play_pcap_image"))) { + ERROR("Scenario specifies a play_pcap_image action, but this version of SIPp does not have PCAP support"); } else if ((ptr = xp_get_value((char *) "play_pcap_video"))) { ERROR("Scenario specifies a play_pcap_video action, but this version of SIPp does not have PCAP support"); #endif diff --git a/src/send_packets.c b/src/send_packets.c index 3113decf3..9dafa0758 100644 --- a/src/send_packets.c +++ b/src/send_packets.c @@ -199,8 +199,8 @@ int send_packets (play_args_t * play_args) memcpy(udp, pkt_index->data, pkt_index->pktlen); port_diff = ntohs (udp->uh_dport) - pkts->base; // modify UDP ports - udp->uh_sport = htons(port_diff + *from_port); - udp->uh_dport = htons(port_diff + *to_port); + udp->uh_sport = htons(port_diff + ntohs(*from_port)); + udp->uh_dport = htons(port_diff + ntohs(*to_port)); if (!media_ip_is_ipv6) { temp_sum = checksum_carry(pkt_index->partial_check + check((u_int16_t *) &(((struct sockaddr_in *)(void *) from)->sin_addr.s_addr), 4) + check((u_int16_t *) &(((struct sockaddr_in *)(void *) to)->sin_addr.s_addr), 4) + check((u_int16_t *) &udp->uh_sport, 4));