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

Capture SCTP traffic in pcaps #2121

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import org.jitsi.nlj.srtp.SrtpTransformers
import org.jitsi.nlj.stats.EndpointConnectionStats
import org.jitsi.nlj.stats.RtpReceiverStats
import org.jitsi.nlj.transform.NodeStatsProducer
import org.jitsi.nlj.transform.node.Node
import org.jitsi.nlj.util.Bandwidth

abstract class RtpReceiver :
Expand Down Expand Up @@ -63,6 +64,8 @@ abstract class RtpReceiver :
abstract fun forceMuteAudio(shouldMute: Boolean)

abstract fun forceMuteVideo(shouldMute: Boolean)

abstract fun getPcapNode(): Node
}

interface RtpReceiverEventHandler {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class RtpReceiverImpl @JvmOverloads constructor(
}
})
}
private val toggleablePcapWriter = ToggleablePcapWriter(logger, "$id-rx")
private val toggleablePcapWriter = ToggleablePcapWriter(logger, "$id-rx", outgoing = false)
private val videoBitrateCalculator = VideoBitrateCalculator(parentLogger)
private val audioBitrateCalculator = BitrateCalculator("Audio bitrate calculator")

Expand All @@ -151,6 +151,8 @@ class RtpReceiverImpl @JvmOverloads constructor(
tccGenerator.addLossListener(lossListener)
}

override fun getPcapNode() = toggleablePcapWriter.newObserverNode()

companion object {
val queueErrorCounter = CountingErrorHandler()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.jitsi.nlj.srtp.SrtpTransformers
import org.jitsi.nlj.stats.EndpointConnectionStats
import org.jitsi.nlj.stats.PacketStreamStats
import org.jitsi.nlj.transform.NodeStatsProducer
import org.jitsi.nlj.transform.node.Node
import org.jitsi.nlj.transform.node.outgoing.OutgoingStatisticsSnapshot

/**
Expand All @@ -46,6 +47,7 @@ abstract class RtpSender :
abstract fun addLossListener(lossListener: LossListener)
abstract fun setFeature(feature: Features, enabled: Boolean)
abstract fun isFeatureEnabled(feature: Features): Boolean
abstract fun getPcapNode(): Node
abstract fun tearDown()

abstract val bandwidthEstimator: BandwidthEstimator
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class RtpSenderImpl(

private val srtpEncryptWrapper = SrtpEncryptNode()
private val srtcpEncryptWrapper = SrtcpEncryptNode()
private val toggleablePcapWriter = ToggleablePcapWriter(logger, "$id-tx")
private val toggleablePcapWriter = ToggleablePcapWriter(logger, "$id-tx", outgoing = true)
private val outgoingPacketCache = PacketCacher()
private val absSendTime = AbsSendTime(streamInformationStore)
private val statsTracker = OutgoingStatisticsTracker()
Expand Down Expand Up @@ -312,6 +312,8 @@ class RtpSenderImpl(
addJson("Bandwidth Estimation", bandwidthEstimator.getStats().toJson())
}

override fun getPcapNode(): Node = toggleablePcapWriter.newObserverNode()

override fun stop() {
running = false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ class Transceiver(
rtpReceiver.forceMuteVideo(shouldMute)
}

fun getReceiverPcapNode() = rtpReceiver.getPcapNode()

fun getSenderPcapNode() = rtpSender.getPcapNode()

/**
* Get stats about this transceiver's pipeline nodes
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class Vp8Packet private constructor(

val hasTL0PICIDX = DePacketizer.VP8PayloadDescriptor.hasTL0PICIDX(buffer, payloadOffset, payloadLength)

@field:Suppress("ktlint:standard:property-naming")
@field:Suppress("ktlint:standard:property-naming", "ktlint:standard:backing-property-naming")
private var _TL0PICIDX = TL0PICIDX
?: DePacketizer.VP8PayloadDescriptor.getTL0PICIDX(buffer, payloadOffset, payloadLength)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class Vp9Packet private constructor(
val isInterPicturePredicted =
DePacketizer.VP9PayloadDescriptor.isInterPicturePredicted(buffer, payloadOffset, payloadLength)

@field:Suppress("ktlint:standard:property-naming")
@field:Suppress("ktlint:standard:property-naming", "ktlint:standard:backing-property-naming")
private var _TL0PICIDX =
TL0PICIDX ?: DePacketizer.VP9PayloadDescriptor.getTL0PICIDX(buffer, payloadOffset, payloadLength)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,11 @@ import kotlin.io.path.Path

class PcapWriter(
parentLogger: Logger,
filePath: Path = Path(directory, "${Random().nextLong()}.pcap")
filePath: Path = Path(directory, "${Random().nextLong()}.pcap"),
private val outgoing: Boolean
) : ObserverNode("PCAP writer") {
constructor(parentLogger: Logger, filePath: String) : this(parentLogger, Path(filePath))
constructor(parentLogger: Logger, filePath: String, outgoing: Boolean) :
this(parentLogger, Path(filePath), outgoing)

private val logger = createChildLogger(parentLogger)
private val lazyHandle = lazy {
Expand All @@ -60,6 +62,10 @@ class PcapWriter(

companion object {
private val localhost = Inet4Address.getByName("127.0.0.1") as Inet4Address
private val remotehost = Inet4Address.getByName("192.0.2.0") as Inet4Address

private val localport = UdpPort(123, "blah")
private val remoteport = UdpPort(123, "blah")
val directory: String by config("jmt.debug.pcap.directory".from(JitsiConfig.newConfig))
}

Expand All @@ -70,18 +76,33 @@ class PcapWriter(
val subBuf = ByteArray(packetInfo.packet.length)
System.arraycopy(packetInfo.packet.buffer, packetInfo.packet.offset, subBuf, 0, packetInfo.packet.length)
udpPayload.rawData(subBuf)
val srchost: Inet4Address
val dsthost: Inet4Address
val srcport: UdpPort
val dstport: UdpPort
if (outgoing) {
srchost = localhost
srcport = localport
dsthost = remotehost
dstport = remoteport
} else {
srchost = remotehost
srcport = remoteport
dsthost = remotehost
dstport = remoteport
}
val udp = UdpPacket.Builder()
.srcPort(UdpPort(123, "blah"))
.dstPort(UdpPort(456, "blah"))
.srcAddr(localhost)
.dstAddr(localhost)
.srcPort(srcport)
.dstPort(dstport)
.srcAddr(srchost)
.dstAddr(dsthost)
.correctChecksumAtBuild(true)
.correctLengthAtBuild(true)
.payloadBuilder(udpPayload)

val ipPacket = IpV4Packet.Builder()
.srcAddr(localhost)
.dstAddr(localhost)
.srcAddr(srchost)
.dstAddr(dsthost)
.protocol(IpNumber.UDP)
.version(IpVersion.IPV4)
.tos(IpV4Rfc1349Tos.newInstance(0))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ import kotlin.io.path.Path

class ToggleablePcapWriter(
private val parentLogger: Logger,
private val prefix: String
private val prefix: String,
private val outgoing: Boolean
) {
private var pcapWriter: PcapWriter? = null
private val pcapLock = Any()
Expand All @@ -37,7 +38,11 @@ class ToggleablePcapWriter(

synchronized(pcapLock) {
if (pcapWriter == null) {
pcapWriter = PcapWriter(parentLogger, Path(PcapWriter.directory, "$prefix-${Date().toInstant()}.pcap"))
pcapWriter = PcapWriter(
parentLogger,
Path(PcapWriter.directory, "$prefix-${Date().toInstant()}.pcap"),
outgoing
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ class DtlsTest : ShouldSpec() {
init {
val dtlsServer = DtlsStack(logger).apply { actAsServer() }
val dtlsClient = DtlsStack(logger).apply { actAsClient() }
val pcapWriter = if (pcapEnabled) PcapWriter(logger, "/tmp/dtls-test.pcap") else null
// TODO: should mark the two directions of DTLS traffic separately in the PCAP
val pcapWriter = if (pcapEnabled) PcapWriter(logger, "/tmp/dtls-test.pcap", true) else null

dtlsClient.remoteFingerprints = mapOf(
dtlsServer.localFingerprintHashFunction to dtlsServer.localFingerprint
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ class EndpointConnectionStatsTest : ShouldSpec() {
stats.rtcpPacketReceived(rrPacket, clock.instant())
context("the rtt") {
should("be updated correctly") {
mostRecentPublishedRtt shouldBe(10.0 plusOrMinus .1)
mostRecentPublishedRtt shouldBe (10.0 plusOrMinus .1)
}
}
}
Expand All @@ -78,7 +78,7 @@ class EndpointConnectionStatsTest : ShouldSpec() {
stats.rtcpPacketReceived(rrPacket, clock.instant())
context("the rtt") {
should("be updated correctly") {
mostRecentPublishedRtt shouldBe(10.0.plusOrMinus(.1))
mostRecentPublishedRtt shouldBe (10.0.plusOrMinus(.1))
}
}
}
Expand Down Expand Up @@ -124,7 +124,7 @@ class EndpointConnectionStatsTest : ShouldSpec() {

context("the rtt") {
should("be updated correctly") {
mostRecentPublishedRtt shouldBe(10.0.plusOrMinus(.1))
mostRecentPublishedRtt shouldBe (10.0.plusOrMinus(.1))
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion jvb/src/main/kotlin/org/jitsi/videobridge/Endpoint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import org.jitsi.nlj.rtp.VideoRtpPacket
import org.jitsi.nlj.srtp.TlsRole
import org.jitsi.nlj.stats.EndpointConnectionStats
import org.jitsi.nlj.transform.node.ConsumerNode
import org.jitsi.nlj.transform.pipeline
import org.jitsi.nlj.util.Bandwidth
import org.jitsi.nlj.util.LocalSsrcAssociation
import org.jitsi.nlj.util.NEVER
Expand Down Expand Up @@ -287,6 +288,14 @@ class Endpoint @JvmOverloads constructor(
recurringRunnableExecutor.registerRecurringRunnable(it)
}

private val receivePcap = transceiver.getReceiverPcapNode()
private val sendPcap = transceiver.getSenderPcapNode()

private val sctpPipeline = pipeline {
node(receivePcap)
node(sctpHandler)
}

/**
* Manages remapping of video SSRCs when enabled.
*/
Expand Down Expand Up @@ -522,7 +531,7 @@ class Endpoint @JvmOverloads constructor(
*/
// TODO(brian): change sctp handler to take buf, off, len
fun dtlsAppPacketReceived(data: ByteArray, off: Int, len: Int) =
sctpHandler.processPacket(PacketInfo(UnparsedPacket(data, off, len)))
sctpPipeline.processPacket(PacketInfo(UnparsedPacket(data, off, len)))

private fun effectiveVideoConstraintsChanged(
oldEffectiveConstraints: EffectiveConstraintsMap,
Expand Down Expand Up @@ -577,6 +586,7 @@ class Endpoint @JvmOverloads constructor(
// Create the SctpManager and provide it a method for sending SCTP data
sctpManager = SctpManager(
{ data, offset, length ->
sendPcap.processPacket(PacketInfo(UnparsedPacket(data, offset, length)))
dtlsTransport.sendDtlsData(data, offset, length)
0
},
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
<jitsi.utils.version>1.0-127-g6c65524</jitsi.utils.version>
<jicoco.version>1.1-140-g8f45a9f</jicoco.version>
<mockk.version>1.13.8</mockk.version>
<ktlint-maven-plugin.version>3.0.0</ktlint-maven-plugin.version>
<ktlint-maven-plugin.version>3.2.0</ktlint-maven-plugin.version>
<maven-shade-plugin.version>3.5.1</maven-shade-plugin.version>
<spotbugs.version>4.6.0</spotbugs.version>
<jersey.version>3.0.10</jersey.version>
Expand Down
1 change: 1 addition & 0 deletions rtp/src/main/kotlin/org/jitsi/rtp/rtp/RtpPacket.kt
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ open class RtpPacket(
private val headerExtensionParser
get() = HeaderExtensionHelpers.getHeaderExtensionParser(extensionsProfileType)

@field:Suppress("ktlint:standard:backing-property-naming")
private val _encodedHeaderExtensions: EncodedHeaderExtensions = EncodedHeaderExtensions()
private val encodedHeaderExtensions: EncodedHeaderExtensions
get() {
Expand Down
4 changes: 2 additions & 2 deletions rtp/src/test/kotlin/org/jitsi/rtp/rtp/RtpHeaderTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -184,13 +184,13 @@ class RtpHeaderTest : ShouldSpec() {
context("csrcs") {
context("get") {
should("work correctly") {
RtpHeader.getCsrcs(headerData, 0) shouldContainInOrder(listOf<Long>(123456, 45678))
RtpHeader.getCsrcs(headerData, 0) shouldContainInOrder (listOf<Long>(123456, 45678))
}
}
context("set") {
should("work correctly") {
RtpHeader.setCsrcs(headerData, 0, listOf<Long>(2468, 1357))
RtpHeader.getCsrcs(headerData, 0) shouldContainInOrder(listOf<Long>(2468, 1357))
RtpHeader.getCsrcs(headerData, 0) shouldContainInOrder (listOf<Long>(2468, 1357))
}
}
}
Expand Down
Loading