|
|
|
|
@ -126,6 +126,10 @@ static err_t tcp_output_cb(void *arg, struct pbuf *p, uint32_t src_ip, uint32_t
|
|
|
|
|
if (len > 2000) return LERR_BUF; |
|
|
|
|
uint8_t buf[2000]; |
|
|
|
|
pbuf_copy_partial(p, buf, len, 0); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP OUT %u.%u.%u.%u→%u.%u.%u.%u len=%u fd=%d", |
|
|
|
|
(uint8_t)(src_ip), (uint8_t)(src_ip>>8), (uint8_t)(src_ip>>16), (uint8_t)(src_ip>>24), |
|
|
|
|
(uint8_t)(dst_ip), (uint8_t)(dst_ip>>8), (uint8_t)(dst_ip>>16), (uint8_t)(dst_ip>>24), |
|
|
|
|
len, proxy->ip_fd); |
|
|
|
|
if (proxy->tun) tun_platform_write(proxy->tun, buf, len); |
|
|
|
|
else if (proxy->ip_fd >= 0) { ssize_t n = write(proxy->ip_fd, buf, len); (void)n; } |
|
|
|
|
return LERR_OK; |
|
|
|
|
@ -168,14 +172,16 @@ static int tcp_proxy_handle_non_tcp(struct tcp_proxy* p, uint8_t* buf, size_t le
|
|
|
|
|
static void proxy_feed_from_transport(struct proxy_conn *pc) { |
|
|
|
|
if (!pc->transport_to_uip || !pc->pcb) return; |
|
|
|
|
int sent_any = 0; |
|
|
|
|
int entries_fed = 0; |
|
|
|
|
while (1) { |
|
|
|
|
uint16_t space = tcp_sndbuf(pc->pcb); |
|
|
|
|
if (space < TCP_MSS / 2) break; |
|
|
|
|
if (space < TCP_MSS / 2) { DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "FEED space=%u<ssthresh break active=%d", space, pc->active); break; } |
|
|
|
|
struct ll_entry *e = queue_data_get(pc->transport_to_uip); |
|
|
|
|
if (!e) break; |
|
|
|
|
uint16_t len = e->len; |
|
|
|
|
if (len > space) len = space; |
|
|
|
|
err_t ret = tcp_write(pc->pcb, e->dgram, len, TCP_WRITE_FLAG_COPY); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "FEED tcp_write len=%u ret=%d active=%d cwnd=%u snd_wnd=%u unsent=%p", len, ret, pc->active, pc->pcb->cwnd, pc->pcb->snd_wnd, (void*)pc->pcb->unsent); |
|
|
|
|
if (ret == LERR_OK) { |
|
|
|
|
sent_any = 1; |
|
|
|
|
if (len >= e->len) { queue_dgram_free(e); queue_entry_free(e); } |
|
|
|
|
@ -209,7 +215,7 @@ static err_t proxy_accept_cb(void *arg, struct tcp_pcb *newpcb, err_t err) {
|
|
|
|
|
tcp_recv(newpcb, proxy_recv_cb); |
|
|
|
|
tcp_sent(newpcb, proxy_sent_cb); |
|
|
|
|
tcp_err(newpcb, proxy_err_cb); |
|
|
|
|
tcp_poll(newpcb, proxy_poll_cb, 2); |
|
|
|
|
tcp_poll(newpcb, proxy_poll_cb, 0); |
|
|
|
|
tcp_nagle_disable(newpcb); |
|
|
|
|
|
|
|
|
|
pc->uip_to_transport = queue_new(p->ua, 0, 0, 0, "uip_to_transport"); |
|
|
|
|
@ -255,12 +261,14 @@ static err_t proxy_recv_cb(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (pc->active) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "RECV active pc=%p len=%u", (void*)pc, p->tot_len); |
|
|
|
|
struct ll_entry *e = entry_from_data(pc->proxy->entry_pool, p->payload, p->tot_len); |
|
|
|
|
if (e) queue_data_put(pc->uip_to_transport, e); |
|
|
|
|
tcp_recved(pcb, p->tot_len); |
|
|
|
|
pbuf_free(p); |
|
|
|
|
} else if (pc->transport && !pc->half_closed) { |
|
|
|
|
uint16_t len = p->tot_len; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "RECV passive pc=%p len=%u transport=%p connected=%d", (void*)pc, len, (void*)pc->transport, ((struct sock_transport*)pc->transport)->connected); |
|
|
|
|
uint8_t *data = u_malloc(len); |
|
|
|
|
if (data) { |
|
|
|
|
pbuf_copy_partial(p, data, len, 0); |
|
|
|
|
@ -313,8 +321,9 @@ static err_t proxy_connected_cb(void *arg, struct tcp_pcb *pcb, err_t err) {
|
|
|
|
|
pc->closing = 1; |
|
|
|
|
return LERR_ABRT; |
|
|
|
|
} |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_SOCKET, "TCP proxy: active connect established to %d.%d.%d.%d:%d", |
|
|
|
|
pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port)); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP proxy: active connect established to %d.%d.%d.%d:%d cwnd=%u snd_wnd=%u snd_buf=%u", |
|
|
|
|
pc->dest_ip[0], pc->dest_ip[1], pc->dest_ip[2], pc->dest_ip[3], ntohs(pc->dest_port), |
|
|
|
|
pcb->cwnd, pcb->snd_wnd, pcb->snd_buf); |
|
|
|
|
proxy_feed_from_transport(pc); |
|
|
|
|
return LERR_OK; |
|
|
|
|
} |
|
|
|
|
@ -326,18 +335,31 @@ static void tcp_proxy_raw_read(int fd, void* arg) {
|
|
|
|
|
(void)fd; struct tcp_proxy* p = (struct tcp_proxy*)arg; |
|
|
|
|
uint8_t buf[2000]; ssize_t n = read(p->ip_fd, buf, sizeof(buf)); |
|
|
|
|
if (n <= 0) return; |
|
|
|
|
if (tcp_proxy_handle_non_tcp(p, buf, n)) return; |
|
|
|
|
if ((size_t)n < 20) return; |
|
|
|
|
uint8_t proto = buf[9]; |
|
|
|
|
uint32_t src_ip, dst_ip; |
|
|
|
|
memcpy(&src_ip, buf + 12, 4); memcpy(&dst_ip, buf + 16, 4); |
|
|
|
|
if (proto != IPPROTO_TCP) return; |
|
|
|
|
uint16_t ip_hdr_len = (buf[0] & 0x0F) * 4; |
|
|
|
|
if ((size_t)n < ip_hdr_len) return; |
|
|
|
|
struct pbuf *pb = pbuf_alloc(PBUF_RAW, (uint16_t)(n - ip_hdr_len)); |
|
|
|
|
if (!pb) return; |
|
|
|
|
pbuf_take(pb, buf + ip_hdr_len, (uint16_t)(n - ip_hdr_len)); |
|
|
|
|
lwip_tcp_input(p->lwip, pb, src_ip, dst_ip); |
|
|
|
|
|
|
|
|
|
size_t offset = 0; |
|
|
|
|
while (offset < (size_t)n) { |
|
|
|
|
size_t remaining = (size_t)n - offset; |
|
|
|
|
if (remaining < 20) break; |
|
|
|
|
uint8_t *pkt = buf + offset; |
|
|
|
|
if (tcp_proxy_handle_non_tcp(p, pkt, remaining)) { offset += remaining; continue; } |
|
|
|
|
uint8_t proto = pkt[9]; |
|
|
|
|
if (proto != IPPROTO_TCP) { offset += remaining; continue; } |
|
|
|
|
uint16_t ip_hdr_len = (pkt[0] & 0x0F) * 4; |
|
|
|
|
uint16_t ip_total = ((uint16_t)pkt[2] << 8) | pkt[3]; |
|
|
|
|
if (ip_hdr_len < 20 || ip_total < ip_hdr_len || (size_t)ip_total > remaining) break; |
|
|
|
|
uint32_t src_ip, dst_ip; |
|
|
|
|
memcpy(&src_ip, pkt + 12, 4); memcpy(&dst_ip, pkt + 16, 4); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP IN %u.%u.%u.%u→%u.%u.%u.%u proto=%u iplen=%u", |
|
|
|
|
(uint8_t)(src_ip), (uint8_t)(src_ip>>8), (uint8_t)(src_ip>>16), (uint8_t)(src_ip>>24), |
|
|
|
|
(uint8_t)(dst_ip), (uint8_t)(dst_ip>>8), (uint8_t)(dst_ip>>16), (uint8_t)(dst_ip>>24), |
|
|
|
|
proto, ip_total); |
|
|
|
|
uint16_t tcp_len = ip_total - ip_hdr_len; |
|
|
|
|
struct pbuf *pb = pbuf_alloc(PBUF_RAW, tcp_len); |
|
|
|
|
if (!pb) break; |
|
|
|
|
pbuf_take(pb, pkt + ip_hdr_len, tcp_len); |
|
|
|
|
lwip_tcp_input(p->lwip, pb, src_ip, dst_ip); |
|
|
|
|
offset += ip_total; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) { |
|
|
|
|
@ -351,11 +373,13 @@ static void tcp_proxy_tun_input(struct ll_queue* q, void* arg) {
|
|
|
|
|
uint8_t proto = ip[9]; |
|
|
|
|
if (proto == IPPROTO_TCP) { |
|
|
|
|
uint16_t ip_hdr_len = (ip[0] & 0x0F) * 4; |
|
|
|
|
if (len >= ip_hdr_len) { |
|
|
|
|
uint16_t ip_total = ((uint16_t)ip[2] << 8) | ip[3]; |
|
|
|
|
if (ip_hdr_len >= 20 && ip_total >= ip_hdr_len && len >= ip_total) { |
|
|
|
|
uint32_t src_ip, dst_ip; |
|
|
|
|
memcpy(&src_ip, ip + 12, 4); memcpy(&dst_ip, ip + 16, 4); |
|
|
|
|
struct pbuf *pb = pbuf_alloc(PBUF_RAW, (uint16_t)(len - ip_hdr_len)); |
|
|
|
|
if (pb) { pbuf_take(pb, ip + ip_hdr_len, (uint16_t)(len - ip_hdr_len)); lwip_tcp_input(p->lwip, pb, src_ip, dst_ip); } |
|
|
|
|
uint16_t tcp_len = ip_total - ip_hdr_len; |
|
|
|
|
struct pbuf *pb = pbuf_alloc(PBUF_RAW, tcp_len); |
|
|
|
|
if (pb) { pbuf_take(pb, ip + ip_hdr_len, tcp_len); lwip_tcp_input(p->lwip, pb, src_ip, dst_ip); } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -445,6 +469,7 @@ static void sock_transport_read_callback(socket_t sock, void* arg) {
|
|
|
|
|
|
|
|
|
|
static void sock_transport_write_callback(socket_t sock, void* arg) { |
|
|
|
|
(void)sock; struct sock_transport* st = (struct sock_transport*)arg; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "WRITE_CB st=%p connected=%d connect_called=%d", (void*)st, st->connected, st->connect_called); |
|
|
|
|
if(!st->connected) { |
|
|
|
|
if(!st->connect_called) return; |
|
|
|
|
int err = 0; socklen_t len = sizeof(err); |
|
|
|
|
@ -475,7 +500,23 @@ static void sock_transport_write_callback(socket_t sock, void* arg) {
|
|
|
|
|
|
|
|
|
|
static void sock_transport_error_callback(socket_t sock, void* arg) { |
|
|
|
|
(void)sock; struct sock_transport* st = (struct sock_transport*)arg; |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: socket error"); sock_transport_close(&st->base); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "ERROR_CB st=%p sock=%d connect_called=%d connected=%d", (void*)st, st->sock, st->connect_called, st->connected); |
|
|
|
|
// For non-blocking connect, EPOLLERR can fire before EPOLLOUT.
|
|
|
|
|
// Check SO_ERROR: if connect succeeded (err==0), trigger write callback.
|
|
|
|
|
if (st->connect_called && !st->connected) { |
|
|
|
|
int err = 0; socklen_t len = sizeof(err); |
|
|
|
|
if (getsockopt(st->sock, SOL_SOCKET, SO_ERROR, &err, &len) == 0) { |
|
|
|
|
if (err == 0) { |
|
|
|
|
sock_transport_write_callback(st->sock, st); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: connect error fd=%d err=%d (%s)", st->sock, err, strerror(err)); |
|
|
|
|
sock_transport_close(&st->base); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_SOCKET, "TCP proxy: socket error fd=%d", st->sock); |
|
|
|
|
sock_transport_close(&st->base); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ====================================================================
|
|
|
|
|
@ -668,13 +709,13 @@ struct tcp_proxy* tcp_proxy_create(struct UTUN_INSTANCE* inst, struct UASYNC* ua
|
|
|
|
|
if (listen_pcb) { |
|
|
|
|
tcp_arg(listen_pcb, p); |
|
|
|
|
tcp_accept(listen_pcb, proxy_accept_cb); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_TRAFFIC, "TCP proxy: listen on %d ok lpcb=%p ctx_listen=%p", |
|
|
|
|
mappings[j].local_port, (void*)listen_pcb, (void*)p->lwip->listen_pcbs); |
|
|
|
|
} |
|
|
|
|
struct tcp_proxy_mapping* m = u_calloc(1, sizeof(struct tcp_proxy_mapping)); |
|
|
|
|
if (m) { m->local_port = port_net; struct in_addr ra; ra.s_addr = inet_addr(mappings[j].remote_ip); |
|
|
|
|
if (m) { m->local_port = mappings[j].local_port; struct in_addr ra; ra.s_addr = inet_addr(mappings[j].remote_ip); |
|
|
|
|
memcpy(m->remote_ip, &ra.s_addr, 4); m->remote_port = htons(mappings[j].remote_port); m->dynamic = 0; |
|
|
|
|
m->next = p->mappings; p->mappings = m; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_TUN, "TCP proxy mapping: %d -> %s:%d%s", mappings[j].local_port, mappings[j].remote_ip, mappings[j].remote_port, |
|
|
|
|
p->has_remote_mappings ? " (remote)" : ""); } |
|
|
|
|
m->next = p->mappings; p->mappings = m; } |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|