# Packet delivery

**URL:** <https://openziti.discourse.group/t/packet-delivery/3908>\
**Category:** Ziti Edge Tunnel\
**Created:** [February 8, 2025, 9:36am UTC](https://openziti.discourse.group/t/packet-delivery/3908 "2025-02-08T09:36:33Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Guardiant314](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/guardiant314/32/2036_2.png) [@Guardiant314](https://openziti.discourse.group/u/Guardiant314)\
**Post date:** [February 8, 2025, 9:36am UTC](https://openziti.discourse.group/t/packet-delivery/3908/1 "2025-02-08T09:36:33Z")

</div>

Hi,

Could you please explain how communication is achieved when the src address in the packet header is IPv4, and the dst address is IPv6?

Here are my service gateway logs:

```auto
(199)[14178.652] DEBUG ziti-sdk:bind.c:386 on_message() received msg ct[ed73] code[0] from www.test-router.com
(199)[14178.652] DEBUG tunnel-cbs:ziti_hosting.c:594 on_hosted_client_connect() hosted_service[B1-1] client[sszglinux1]: received app_data_json='{"connType":null,"dst_protocol":"tcp","dst_ip":"100.64.0.7","dst_port":"80","src_protocol":"tcp","src_ip":"100.64.0.1","src_port":"39514"}'
(199)[14178.652] VERBOSE tunnel-cbs:ziti_hosting.c:376 compute_dst_ip_or_hn() using address from config
(199)[14178.652] INFO tunnel-cbs:ziti_hosting.c:649 on_hosted_client_connect() hosted_service[B1-1] client[sszglinux1] client_src_addr[tcp:100.64.0.1:39514] dst_addr[tcp:df:e22:c03::3:5000]: incoming connection 
(199)[14178.653] DEBUG tunnel-cbs:ziti_hosting.c:721 on_hosted_client_connect_resolved() hosted_service[B1-1] client[sszglinux1] client_src_addr[tcp:100.64.0.1:39514] initiating connection to tcp:df:e22:c03::3:5000
(199)[14178.654] DEBUG tunnel-cbs:ziti_hosting.c:204 complete_hosted_tcp_connection() hosted_service[B1-1], client[sszglinux1] client_src_addr[tcp:100.64.0.1:39514]: connected to server tcp:df:e22:c03::3:5000

```

Thank you very much for your support!

---

<div class="post-metadata">

**Author:** ![TheLumberjack](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/thelumberjack/32/113_2.png) [@TheLumberjack](https://openziti.discourse.group/u/TheLumberjack)\
**Post date:** [February 8, 2025, 12:44pm UTC](https://openziti.discourse.group/t/packet-delivery/3908/2 "2025-02-08T12:44:54Z")

</div>

Each tunneler will establish it's own TCP session with a router first. The remote tunneler will establish an independent TCP session to whatever target it's instructed to. So the TCP sessions are entirely independent.

OpenZiti tunnelers specific function is to tunnel bits, not IP packets. So in the client side tunneler, the actual payload is extracted and put onto the overlay not the IP packet. The overlay then routes the payload to the termination point on the OpenZiti overlay. In this case, that's another tunneler. When the data arrives at the destination tunneler, there's configuration (host.v1/v2) that instructs the tunneler what to do with the payload. Here, the instruction is "open s new TCP session to. IPv6 address and send this data".

Does that clear it up or did I miss something in your question?

---

<div class="post-metadata">

**Author:** ![Guardiant314](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/guardiant314/32/2036_2.png) [@Guardiant314](https://openziti.discourse.group/u/Guardiant314)\
**Post date:** [March 25, 2025, 12:40pm UTC](https://openziti.discourse.group/t/packet-delivery/3908/3 "2025-03-25T12:40:37Z")

</div>

Hi TheLumberjack，

Thank you very much for your explanation! 😃

The tunneler intercepts traffic using TUN and passes the intercepted packets to the lwIP stack via the netif\_shim\_input function. During this process, the packets are converted to the pbuf\_raw layer. Does this mean that the intercepted traffic discards the packet headers?

After processing, the packets are forwarded from the protocol stack to the virtual network interface using the netif\_shim\_output function. In this step, the output is encapsulated as a pbuf\_transport layer. Does this step encapsulate the protocol, IP, and port? Or is the encapsulated data still just the payload?

---

<div class="post-metadata">

**Author:** ![scareything](https://yyz2.discourse-cdn.com/free1/user_avatar/openziti.discourse.group/scareything/32/106_2.png) [@scareything](https://openziti.discourse.group/u/scareything)\
**Post date:** [March 25, 2025, 1:53pm UTC](https://openziti.discourse.group/t/packet-delivery/3908/4 "2025-03-25T13:53:03Z")

</div>

> [@Guardiant314](#):
>
> During this process, the packets are converted to the pbuf\_raw layer. Does this mean that the intercepted traffic discards the packet headers?

The header information from intercepted packets is ultimately discarded, but not at the point you're referring to. `netif_shim_input` reads the packet (bytes) from the tun interface and passes the bytes to `on_packet`. It's also helpful to know that lwip's `pbuf` type supports the concept of pointing at (and having space for) [different layers](https://www.nongnu.org/lwip/2_1_x/group__pbuf.html#gaee1baa59bb2f85ba575b5a8619ac1ebf).

`on_packet` allocates a RAW `pbuf` for interacting with lwip and copies the packet bytes into it (with `pbuf_take`). If the allocation and copy are successful, the `pbuf`, which still contains the IP header, is passed to lwip via `netif->input` (which was [previously set up](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/ziti_tunnel.c#L551) to point to [ip\_input()](https://www.nongnu.org/lwip/2_1_x/group __lwip__ nosys.html#ga3c420dab0c6760df099a2d688fa42a26)

`ip_input()` figures out if the packet is UDP or TCP and calls the appropriate receive callback that's implemented by the tunneler SDK (that will be [recv\_udp](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_udp.c#L125) or [recv\_tcp](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_tcp.c#L319)). These receive functions inspect the packet header to determine if it maps to one of the ziti service intercept configurations that the tunneler is aware of, and if so a new overlay connection is created.

It's possible that the received packet is already associated with an intercepted connection - in which case the receive function passes the packet back to lwip where it will be dispatched to the appropriate data callback [on\_udp\_client\_data](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_udp.c#L88) or [on\_tcp\_client\_data](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_tcp.c#L133). The data callbacks receive a `pbuf` that is pointing to the payload of the originally received packet. The header information isn't all that helpful here, since lwip also calls the data callback with the IP connection that the packet is associated with. At this point the only thing that needs to happen is to write the payload to the associated ziti connection.

> [@Guardiant314](#):
>
> After processing, the packets are forwarded from the protocol stack to the virtual network interface using the netif\_shim\_output function. In this step, the output is encapsulated as a pbuf\_transport layer. Does this step encapsulate the protocol, IP, and port? Or is the encapsulated data still just the payload?

`netif_shim_output` is the function that we've told lwip to call when there is data that needs to be delivered on intercepted connections. When lwip calls `netif_shim_output`, it passes a `pbuf` that's pointing at the IP layer. By the time lwip calls this function, it has constructed a complete IP packet using the connection information and the payloads that are sent in [tunneler\_udp\_write](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_udp.c#L257) and [tunneler\_tcp\_write](https://github.com/openziti/ziti-tunnel-sdk-c/blob/main/lib/ziti-tunnel/tunnel_tcp.c#L196).
