### Description
Please check attached pcap. [siptrace.pcap.gz](https://github.com/kamailio/kamailio/files/3688791/siptrace.pcap.gz)
Kamailio send INVITE (packet 1) from port `TCP/42394`. SIP trace encapsulate INVITE (packet 2) and encodes Kamailio source port `TCP/5060`
#### Debugging Data HEP header parsed by lua [disector](https://github.com/sipcapture/hep-wireshark) ``` HEP3 Protocol HEP ID: HEP3 Length (Bytes): 1574 Protocol family: IPv4 Protocol ID: TCP Source IPv4 address: 172.21.0.131 Destination IPv4 address: 192.168.57.11 Source port: 5060 Destination port: 5060 Timestamp: 1570150645 Timestamp us: 740165 Protocol Type: SIP Capture ID: 1 Payload [truncated]: INVITE sip:911@192.168.57.11;transport=tcp SIP/2.0\r\nRecord-Route: sip:172.21.0.131;transport=tcp;lr=on;ftag=KvNj7jXrmK89p\r\nVia: SIP/2.0/TCP 172.21.0.131;branch=z9hG4bK7c2d.2d8104273b7063309f5694ac963c9ad8.0;i=21 ``` [HEP format description located here](https://github.com/sipcapture/HEP/blob/master/docs/HEP3_Network_Protocol_Spe...).
### Additional Information
* **Kamailio Version** - output of `kamailio -v`
``` / # kamailio -v version: kamailio 5.1.6 (x86_64/linux) flags: STATS: Off, USE_TCP, USE_TLS, USE_SCTP, TLS_HOOKS, USE_RAW_SOCKS, DISABLE_NAGLE, USE_MCAST, DNS_IP_HACK, SHM_MEM, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, TLSF_MALLOC, DBG_SR_MEMORY, USE_FUTEX, FAST_LOCK-ADAPTIVE_WAIT, USE_DNS_CACHE, USE_DNS_FAILOVER, USE_NAPTR, USE_DST_BLACKLIST, HAVE_RESOLV_RES ADAPTIVE_WAIT_LOOPS=1024, MAX_RECV_BUFFER_SIZE 262144 MAX_URI_SIZE 1024, BUF_SIZE 65535, DEFAULT PKG_SIZE 8MB poll method support: poll, epoll_lt, epoll_et, sigio_rt, select. id: unknown compiled on 11:01:40 Oct 4 2018 with gcc 6.4.0 ```
* **Operating System**: Docker container Userland ``` / # cat /etc/os-release NAME="Alpine Linux" ID=alpine VERSION_ID=3.8.1 PRETTY_NAME="Alpine Linux v3.8" HOME_URL="http://alpinelinux.org" BUG_REPORT_URL="http://bugs.alpinelinux.org" ``` Kernel Amazon Linux 2 ``` / # uname -a Linux proxy-dc0-0 4.14.128-112.105.amzn2.x86_64 #1 SMP Wed Jun 19 16:53:40 UTC 2019 x86_64 Linux ```
The address is the socket kamailio is listening on, which is typically ip:5060.
With tcp the source port of connect() cannot be easily ensured (only with latest kernels when re-using the port option is enabled).
So this is by design, otherwise there will be different ports for each connection.
If you want to change, that's fine, you can make pull request so other can review, however add a modparam to control this behaviour, with the default option to be like it is now.
Closed #2092.
Thank you Daniel Could you give me reference to code where outbound TCP socket created/bound. I will try debug.
So this is by design, otherwise there will be different ports for each connection.
I do not mind that different ports are used. I am for sending the correct information about the source port
Here is relevant page how to get source port for outbound connection. https://stackoverflow.com/questions/47095534/how-to-get-local-source-port-fr...
How to bind selected port for outbound connection. https://github.com/openssh/openssh-portable/pull/130
Daniel @miconda could you look this links. Think first allow resolve issue. Second link will allow send TCP outbound messages from kamailio `socket` port instead of random port.
Hello Bastian @btriller Could you looks this issue also.
Example how get outbound socket port ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>
#define SERVER_ADDR "172.217.160.99" #define SERVER_PORT 80
int main() { char myIP[16]; unsigned int myPort; struct sockaddr_in server_addr, my_addr; int sockfd;
// Connect to server if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("Can't open stream socket."); exit(-1); }
// Set server_addr bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR); server_addr.sin_port = htons(SERVER_PORT);
// Connect to server if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) { perror("Connect server error"); close(sockfd); exit(-1); }
// Get my ip address and port bzero(&my_addr, sizeof(my_addr)); socklen_t len = sizeof(my_addr); getsockname(sockfd, (struct sockaddr *) &my_addr, &len); inet_ntop(AF_INET, &my_addr.sin_addr, myIP, sizeof(myIP)); myPort = ntohs(my_addr.sin_port);
printf("Local ip address: %s\n", myIP); printf("Local port : %u\n", myPort);
return 0; } ```
Found at https://gist.github.com/listnukira/4045436
sergey-safarov left a comment (kamailio/kamailio#2092)
Here is `Cursor` analyse of the issue.
# Siptrace outbound TCP/TLS source port fix
## 1. Root cause
**Why the wrong port is reported**
- For **outbound** TCP/TLS, the OS assigns an **ephemeral local port** when the connection is created (`connect()`). The real source address is only known after the socket is connected (and can be read with `getsockname()`). - Siptrace (and any subscriber to `SREV_NET_DATA_SENT`) receives a **dest_info_t** whose **send_sock** points to a **socket_info_t**. - **socket_info_t** describes a **listening** socket (e.g. `tcp:1.2.3.4:5060`). It is the “hint” used to choose the outbound interface, not the actual socket used for the connection. - So siptrace uses `dst->send_sock->sock_str` (or `useinfo.sock_str`) and therefore always logs the **listening** address/port, not the **connected** socket’s local address/port.
**Where the real address actually is**
- In [src/core/tcp_main.c](src/core/tcp_main.c), `tcp_do_connect()` already gets the real local address with `getsockname()` and stores it in `res_local_addr` (lines 1456–1461, 1475, 1504). - That value is written into the **tcp_connection** as `c->rcv.dst_ip` and `c->rcv.dst_port` (and `c->rcv.proto`). For outbound connections, `rcv.dst_*` is the **local** endpoint (see [src/core/tcp_conn.h](src/core/tcp_conn.h) macros `TCP_RCV_LADDR`, `TCP_RCV_LPORT`). - So the correct source address **exists** in the TCP layer (`tcp_connection->rcv`) but is **never passed** to the code that raises `SREV_NET_DATA_SENT`. The event is raised in [src/core/forward.h](src/core/forward.h) (lines 349–356) with `netinfo.dst = dst` only; `dst->send_sock` is still the listening socket.
```mermaid flowchart LR subgraph core [Core] msg_send["msg_send_buffer()"] tcp_send["tcp_send(dst)"] conn["tcp_connection c"] event["SREV_NET_DATA_SENT"] msg_send --> tcp_send tcp_send --> conn tcp_send --> event event -->|"nd->dst (send_sock = listen)"| siptrace end subgraph siptrace [siptrace] siptrace["siptrace_net_data_sent()"] end conn -.->|"c->rcv has real local addr (not passed)"| conn ```
---
## 2. Relevant code paths
**Where “from” is set for outbound**
| Path | File / function | Current source of “from” | | ----------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------- | | Low-level send event | [siptrace.c](src/modules/siptrace/siptrace.c) `siptrace_net_data_sent()` (lines 2456–2475) | `new_dst.send_sock->sock_str` or `useinfo.sock_str` | | TM/request-out callback | [siptrace.c](src/modules/siptrace/siptrace.c) `sip_trace()` with `get_onsend_info()` (lines 1379–1388) | `snd_inf->send_sock->sock_str` | | Same for HEP | [siptrace_hep.c](src/modules/siptrace/siptrace_hep.c) (e.g. line 674) | `dst.send_sock->port_no` |
**Core flow**
- [forward.h](src/core/forward.h): `msg_send_buffer()` calls `tcp_send(dst, from, ...)` then `sr_event_exec(SREV_NET_DATA_SENT, &evp)` with `evp.data = &netinfo`, `netinfo.dst = dst`. - [tcp_main.c](src/core/tcp_main.c): `tcp_send()` either finds an existing connection (`tcpconn_lookup` / `tcpconn_get`) or creates one (`tcpconn_connect` → `tcp_do_connect`). In both cases it has a `struct tcp_connection *c` with `c->rcv.dst_ip`, `c->rcv.dst_port`, `c->rcv.proto` set to the **actual local** address for outbound.
---
## 3. Optional way to fix it
**Idea:** Have the core record the **actual local address** used for the send in **dest_info_t**, and have siptrace use it for TCP/TLS when present.
**A. Core changes**
1. **Extend dest_info_t** in [src/core/ip_addr.h](src/core/ip_addr.h) with an optional “sent local” address, so the event can carry the real source without holding a pointer to the connection (avoid lifetime issues). For example: - Add a small struct used only when the send is TCP/TLS and the connection is known, e.g. `struct { struct ip_addr ip; unsigned short port; char proto; int set; } sent_local;` (or a pointer to a copy, or a `receive_info_t *` with clear lifetime rules). The important part is to **copy** ip/port/proto from `c->rcv`, not to store a pointer to `c->rcv`. 2. **In tcp_send()** in [src/core/tcp_main.c](src/core/tcp_main.c): whenever a connection `c` is used to send (either found or newly created), **before** returning success, copy the local address from `c->rcv` into `dst->sent_local` (or the chosen field): `dst_ip` → ip, `dst_port` → port, `proto` → proto. Do this for both TCP and TLS (and any other stream that uses the same convention). Do **not** store a pointer to `c` or `c->rcv`; the connection may be shared or closed after return. 3. **No change to the event itself:** `SREV_NET_DATA_SENT` already receives `netinfo.dst`; once `dst` carries the optional “sent local” info, subscribers can use it.
**B. Siptrace (and HEP) changes**
1. **siptrace_net_data_sent()** in [src/modules/siptrace/siptrace.c](src/modules/siptrace/siptrace.c): when building `sto.fromip` for outbound: - If `nd->dst->sent_local.set` (or equivalent) is set and proto is TCP/TLS, format `sto.fromip` as `proto:ip:port` from `sent_local` (using e.g. `ip_addr2a(&nd->dst->sent_local.ip)` and the port/proto). - Otherwise keep current behavior: use `trace_local_ip` if set, else `send_sock->sock_str` / `useinfo.sock_str`. 2. **TM/onsend path** in [siptrace.c](src/modules/siptrace/siptrace.c) (`sip_trace()` with `get_onsend_info()`): this runs **before** the actual send, so at that moment there is no connection yet and no “sent local” address. So this path will continue to show the listening socket for TCP/TLS unless the flow is changed (e.g. callback after send with updated dest_info). The plan does not require changing this path; the main fix is for `SREV_NET_DATA_SENT`. 3. **HEP** in [siptrace_hep.c](src/modules/siptrace/siptrace_hep.c): wherever the source port is taken from `send_sock->port_no`, add a branch: if “sent local” is set for this dest, use its port (and ip/proto) for the HEP payload instead.
**C. Edge cases**
- **UDP:** No connection; leave `sent_local` unset and keep using `send_sock` as today. - **SCTP:** Same as UDP for the typical case (listening socket); only extend “sent local” for TCP/TLS if that matches the current design. - **Connection created in another process:** If in some builds the send is forwarded to another process and `tcp_send()` in the caller never sees the connection, that path would not set `sent_local`; siptrace would fall back to `send_sock` (current behavior). So behavior stays backward compatible.
---
## 4. Summary
- **Root cause:** Siptrace uses `dest_info_t.send_sock`, which is the **listening** socket. For outbound TCP/TLS the real source is the **connected** socket’s local address (ephemeral port), which lives only in `tcp_connection->rcv` and is not exposed to the event. - **Fix:** Core copies the actual local address from the connection into `dest_info_t` (e.g. `sent_local`) when sending over TCP/TLS; siptrace (and HEP) use this when present and fall back to `send_sock` otherwise. No change to event semantics; only an optional extra field and siptrace/HEP logic to prefer it for TCP/TLS.
miconda left a comment (kamailio/kamailio#2092)
Do not post on closed issues, specially that are closed for very long time -- open a new one, code based has changed.
Also, I am of the opinion that AI generated reports are long and can waste time for reviewers. Try to make a concise summary yourself, after asserting that whatever AI generated is valid, do not just post what AI generates.