The GPRS-NS/BSSGP filter assumed a fixed 20-byte IPv4 header (IP_LEN) when locating the UDP header and payload. When the captured packet carries IPv4 options (ip_hl > 5), udp_data/payload_data pointed into the middle of the headers and check_gprs() parsed garbage, classifying packets incorrectly.
Use the actual header length from ip_hl, reject malformed headers (ip_hl < 5), and re-validate that the larger headers fit within the captured length before computing the payload.
tls: do not treat GNUTLS_E_AGAIN/INTERRUPTED as fatal on read
osmo_tls_client_bfd_cb() treated any non-positive return from gnutls_record_recv() as a fatal error and tore down the session. On a non-blocking socket gnutls_record_recv() can return GNUTLS_E_AGAIN or GNUTLS_E_INTERRUPTED (both negative but non-fatal), which would drop an otherwise healthy TLS session. Handle them as retryable, mirroring the existing logic in tls_write().
server: vty: validate rotate-localtime modulus against the new interval
apply_rotate_localtime() computed the maximum allowed modulus from pcap_server->rotate_localtime.intv, the currently-stored (old) interval, rather than the intv argument being applied. On first configuration the stored interval is the default 0, so the switch hit the default case and rejected an otherwise valid command; when changing intervals the modulus was bounds-checked against the wrong interval. Switch on intv instead.
server: fix NULL deref of file_hdr_msg when store is disabled
When a connection has storing disabled (no store), conn->file_hdr_msg is never populated. The previous link-header handling skipped the first branch (gated on conn->store) and fell through to the comparison branch, which dereferenced the still-NULL conn->file_hdr_msg, crashing the server on the first PKT_LINK_HDR from such a client.
Gate the whole header tracking on conn->store and simply free the message when not storing, since osmo_pcap_conn_restart_trace() already no-ops in that case.