Skip to content

Aravis gv_auto_packet_size() Bug — Returns Sizes Above Path MTU #1065

@Xsez

Description

@Xsez

Summary

arv_gv_device_auto_packet_size() negotiates packet sizes that exceed the network path MTU, resulting in IP fragmentation and 3–7x performance degradation for GigE Vision streaming. Reproduced on two different camera vendors (OMRON Sentech and Basler) — this is a fundamental aravis issue, not vendor-specific.

Environment

  • aravis: 0.8.x (PyGObject bindings)
  • Cameras tested:
    • OMRON SENTECH STC-MCS500POE (2448×2048, BayerRG8, GigE Vision, FW 20.50)
    • Basler acA1600-60gm (1600×1200, Mono8, GigE Vision, FW 106203-16 / V3.15-11)
  • NIC: Intel I219-V (PCI 00:1f.6), driver e1000e, MTU set to 9000 (jumbo frames)
  • OS: Debian GNU/Linux 13 (trixie), kernel 6.12.74
  • Network topology: Camera → PoE injector → managed switch → server. The switch has one uplink to the main network for remote access but carries negligible traffic — effectively a near-direct link. No router between camera and server.

Reproduction

import gi
gi.require_version('Aravis', '0.8')
from gi.repository import Aravis

# --- Sentech ---
cam = Aravis.Camera.new('192.168.1.199')
auto_size = cam.gv_auto_packet_size()
print(f"Sentech auto: {auto_size}")   # Output: 12316  (MTU 9000!)
del cam

# --- Basler ---
cam = Aravis.Camera.new('192.168.1.145')
auto_size = cam.gv_auto_packet_size()
print(f"Basler auto: {auto_size}")    # Output: 12316  (same!)
del cam

Both cameras return exactly 12316 — well above the NIC MTU of 9000.

Evidence

Sentech STC-MCS500POE (2448×2048 BayerRG8, 5.01 MB/frame)

Packet Size Avg Grab Time Notes
1504 111.8 ms Default (MTU 1500)
8228 93.2 ms Within MTU 9000, no fragmentation
12316 263.7 ms Auto-negotiated, above MTU → 2.8x slower

Basler acA1600-60gm (1600×1200 Mono8, 1.92 MB/frame)

Packet Size Avg Grab Time Notes
1500 23.6 ms Default (MTU 1500)
8228 22.7 ms Within MTU 9000, optimal — 109 MB/s throughput
12316 158.2 ms Auto-negotiated, above MTU → 7x slower
16404 152.8 ms Camera max (16404), above MTU → same degradation

The Basler results are particularly clear: 8228 and 1500 perform nearly identically (the image is small enough that packet overhead is minimal), but exceeding MTU causes catastrophic 7x slowdown.

Root Cause Analysis

The auto_packet_size() implementation in arvgvdevice.c does set ArvGevSCPSDoNotFragment = TRUE before the binary search. However, on this network topology (switch, no router), the DF bit doesn't help:

  1. DF only triggers ICMP "Fragmentation Needed" from routers. A Layer 2 switch forwards Ethernet frames — it doesn't inspect IP headers or enforce DF. There is no router in the camera-to-server path.
  2. The OS network stack reassembles fragments transparently — the test packet arrives successfully even though it was fragmented at the IP layer. The binary search sees "success" and keeps increasing.
  3. Both cameras report GevSCPSDoNotFragment support (Basler defaults it to True, Sentech doesn't expose it), yet both produce the same 12316 result. The DF flag is set on the wire but has no enforcer.

The test_packet_check() function only verifies "did a packet of the right size arrive?" — it cannot detect whether the packet was fragmented and reassembled en route.

Why exactly 12316?

Both cameras — different vendors, different firmware, different packet size ranges (Sentech: 72–16260, Basler: 220–16404) — converge on exactly 12316. This is the binary search ceiling where IP reassembly still reliably succeeds within aravis's test timeout. Above ~12–13 KB, reassembly latency or fragment loss causes the test to fail, stopping the search. The value is determined by OS reassembly behavior, not by any meaningful network parameter.

Impact

  • 3–7x streaming performance degradation depending on image size
  • Affects any setup where the network path lacks a router to enforce DF (common in industrial camera setups which use switches or direct connections)
  • Cameras with GevSCPSPacketSize max above NIC MTU are always affected
  • Users relying on default auto-negotiation get silently degraded performance
  • The issue is invisible — no errors, no warnings, just slow transfers

Workaround

Set GvPacketSizeAdjustment.NEVER before creating the stream, and manually set packet size to a value within the NIC MTU:

cam.gv_set_packet_size_adjustment(Aravis.GvPacketSizeAdjustment.NEVER)
cam.gv_set_packet_size(8228)  # Must be <= NIC MTU - 28 (IP+UDP+GVSP headers)
stream = cam.create_stream(None, None)

Suggested Fix

The auto-negotiation should additionally verify that the negotiated packet size does not exceed the interface MTU of the host NIC. This can be queried from the OS via ioctl(SIOCGIFMTU) on Linux (the socket/interface is already available in aravis's GigE device code). The negotiated size should be capped at min(camera_max, nic_mtu - 28).

Alternatively, test_packet_check() could measure round-trip timing — fragmented packets take measurably longer than unfragmented ones, and a timing threshold could detect this.

Additional aravis API issue

cam.set_trigger("Off") fails on both Sentech and Basler cameras:

  • Sentech: tries to set TriggerSource to "Off", which isn't a valid enum
  • Basler: same error — arv-gc-error-quark: [TriggerSource] 'Off' not an entry

The workaround is to write TriggerMode=Off directly via GenICam:

dev = cam.get_device()
dev.set_string_feature_value("TriggerMode", "Off")

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions