📄 mod-tcp.c
字号:
/* Copyright (c) 2006, 2007 Dmitry Butskoy <buc@citadel.stu.neva.ru> License: GPL v2 or any later See COPYING for the status of this software.*/#include <stdlib.h>#include <unistd.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/poll.h>#include <netinet/icmp6.h>#include <netinet/ip_icmp.h>#include <netinet/in.h>#include <netinet/ip.h>#include <netinet/ip6.h>#include <netinet/tcp.h>#include "traceroute.h"#ifndef IP_MTU#define IP_MTU 14#endifstatic sockaddr_any dest_addr = {{ 0, }, };static unsigned int dest_port = 0;static int raw_sk = -1;static int last_ttl = 0;static u_int8_t buf[1024]; /* enough, enough... */static size_t csum_len = 0;static struct tcphdr *th = NULL;#define TH_FLAGS(TH) (((u_int8_t *) (TH))[13])#define TH_FIN 0x01#define TH_SYN 0x02#define TH_RST 0x04#define TH_PSH 0x08#define TH_ACK 0x10#define TH_URG 0x20#define TH_ECE 0x40#define TH_CWR 0x80static int flags = 0; /* & 0xff == tcp_flags ... */static int sysctl = 0;static unsigned int mss = 0;#define FL_FLAGS 0x100#define FL_SACK 0x200#define FL_TSTAMP 0x400#define FL_WSCALE 0x800static int set_tcp_flags (CLIF_option *optn, char *arg) { char *q; unsigned long value; value = strtoul (arg, &q, 0); if (q == arg) return -1; flags = (value & 0xff) | FL_FLAGS; return 0;}static int set_flag (CLIF_option *optn, char *arg) { flags |= (unsigned int) optn->data; return 0;}static CLIF_option tcp_options[] = { { 0, "syn", 0, "Set tcp flag SYN (default if no other " "tcp flags specified)", set_flag, (void *) TH_SYN, 0, 0 }, { 0, "ack", 0, "Set tcp flag ACK,", set_flag, (void *) TH_ACK, 0, 0 }, { 0, "fin", 0, "FIN,", set_flag, (void *) TH_FIN, 0, 0 }, { 0, "rst", 0, "RST,", set_flag, (void *) TH_RST, 0, 0 }, { 0, "psh", 0, "PSH,", set_flag, (void *) TH_PSH, 0, 0 }, { 0, "urg", 0, "URG,", set_flag, (void *) TH_URG, 0, 0 }, { 0, "ece", 0, "ECE,", set_flag, (void *) TH_ECE, 0, 0 }, { 0, "cwr", 0, "CWR", set_flag, (void *) TH_CWR, 0, 0 }, { 0, "flags", "NUM", "Set tcp flags exactly to value %s", set_tcp_flags, 0, 0, CLIF_ABBREV }, { 0, "ecn", 0, "Send syn packet with tcp flags ECE and CWR " "(for Explicit Congestion Notification, rfc3168)", set_flag, (void *) (TH_SYN | TH_ECE | TH_CWR), 0, 0 }, { 0, "sack", 0, "Use sack option for tcp", set_flag, (void *) FL_SACK, 0, 0 }, { 0, "timestamps", 0, "Use timestamps option for tcp", set_flag, (void *) FL_TSTAMP, 0, CLIF_ABBREV }, { 0, "window_scaling", 0, "Use window_scaling option for tcp", set_flag, (void *) FL_WSCALE, 0, CLIF_ABBREV }, { 0, "sysctl", 0, "Use current sysctl (/proc/sys/net/*) setting " "for the tcp options and ecn. Always set by default " "(with \"syn\") if nothing else specified", CLIF_set_flag, &sysctl, 0, 0 }, { 0, "mss", "NUM", "Use value of %s for maxseg tcp option (when syn)", CLIF_set_uint, &mss, 0, 0 }, CLIF_END_OPTION};#define SYSCTL_PREFIX "/proc/sys/net/ipv4/tcp_"static int check_sysctl (const char *name) { int fd, res; char buf[sizeof (SYSCTL_PREFIX) + strlen (name) + 1]; u_int8_t ch; strcpy (buf, SYSCTL_PREFIX); strcat (buf, name); fd = open (buf, O_RDONLY, 0); if (fd < 0) return 0; res = read (fd, &ch, sizeof (ch)); close (fd); if (res == sizeof (ch)) return (ch == '0') ? 0 : 1; return 0;}static int tcp_init (const sockaddr_any *dest, unsigned int port_seq, size_t *packet_len_p) { int af = dest->sa.sa_family; sockaddr_any src; int mtu; socklen_t len; u_int8_t *ptr; u_int16_t *lenp; dest_addr = *dest; dest_addr.sin.sin_port = 0; /* raw sockets can be confused */ if (!port_seq) port_seq = DEF_TCP_PORT; dest_port = htons (port_seq); /* Create raw socket for tcp */ raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); if (raw_sk < 0) error ("socket"); tune_socket (raw_sk); /* including bind, if any */ if (connect (raw_sk, &dest_addr.sa, sizeof (dest_addr)) < 0) error ("connect"); len = sizeof (src); if (getsockname (raw_sk, &src.sa, &len) < 0) error ("getsockname"); len = sizeof (mtu); if (getsockopt (raw_sk, af == AF_INET ? SOL_IP : SOL_IPV6, af == AF_INET ? IP_MTU : IPV6_MTU, &mtu, &len) < 0 || mtu < 576 ) mtu = 576; /* mss = mtu - headers */ mtu -= af == AF_INET ? sizeof (struct iphdr) : sizeof (struct ip6_hdr); mtu -= sizeof (struct tcphdr); if (!raw_can_connect ()) { /* work-around for buggy kernels */ close (raw_sk); raw_sk = socket (af, SOCK_RAW, IPPROTO_TCP); if (raw_sk < 0) error ("socket"); tune_socket (raw_sk); /* but do not connect it... */ } use_recverr (raw_sk); add_poll (raw_sk, POLLIN | POLLERR); /* Now create the sample packet. */ if (!flags) sysctl = 1; if (!(flags & (FL_FLAGS | 0xff))) { /* no any tcp flag set */ flags |= TH_SYN; if (sysctl && check_sysctl ("ecn")) flags |= TH_ECE | TH_CWR; } if (sysctl) { if (check_sysctl ("sack")) flags |= FL_SACK; if (check_sysctl ("timestamps")) flags |= FL_TSTAMP; if (check_sysctl ("window_scaling")) flags |= FL_WSCALE; } /* For easy checksum computing: saddr daddr length protocol tcphdr tcpoptions */ ptr = buf; if (af == AF_INET) { len = sizeof (src.sin.sin_addr); memcpy (ptr, &src.sin.sin_addr, len); ptr += len; memcpy (ptr, &dest_addr.sin.sin_addr, len); ptr += len; } else { len = sizeof (src.sin6.sin6_addr); memcpy (ptr, &src.sin6.sin6_addr, len); ptr += len; memcpy (ptr, &dest_addr.sin6.sin6_addr, len); ptr += len; } lenp = (u_int16_t *) ptr; ptr += sizeof (u_int16_t); *((u_int16_t *) ptr) = htons ((u_int16_t) IPPROTO_TCP); ptr += sizeof (u_int16_t); /* Construct TCP header */ th = (struct tcphdr *) ptr; th->source = 0; /* temporary */ th->dest = dest_port; th->seq = 0; /* temporary */ th->ack_seq = 0; th->doff = 0; /* later... */ TH_FLAGS(th) = flags & 0xff; th->window = htons (4 * mtu); th->check = 0; th->urg_ptr = 0; /* Build TCP options */ ptr = (u_int8_t *) (th + 1); if (flags & TH_SYN) { *ptr++ = TCPOPT_MAXSEG; /* 2 */ *ptr++ = TCPOLEN_MAXSEG; /* 4 */ *((u_int16_t *) ptr) = htons (mss ? mss : mtu); ptr += sizeof (u_int16_t); } if (flags & FL_TSTAMP) { if (flags & FL_SACK) { *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ *ptr++ = TCPOLEN_SACK_PERMITTED;/* 2 */ } else { *ptr++ = TCPOPT_NOP; /* 1 */ *ptr++ = TCPOPT_NOP; /* 1 */ } *ptr++ = TCPOPT_TIMESTAMP; /* 8 */ *ptr++ = TCPOLEN_TIMESTAMP; /* 10 */ *((u_int32_t *) ptr) = random_seq (); /* really! */ ptr += sizeof (u_int32_t); *((u_int32_t *) ptr) = (flags & TH_ACK) ? random_seq () : 0; ptr += sizeof (u_int32_t); } else if (flags & FL_SACK) { *ptr++ = TCPOPT_NOP; /* 1 */ *ptr++ = TCPOPT_NOP; /* 1 */ *ptr++ = TCPOPT_SACK_PERMITTED; /* 4 */ *ptr++ = TCPOLEN_SACK_PERMITTED; /* 2 */ } if (flags & FL_WSCALE) { *ptr++ = TCPOPT_NOP; /* 1 */ *ptr++ = TCPOPT_WINDOW; /* 3 */ *ptr++ = TCPOLEN_WINDOW; /* 3 */ *ptr++ = 2; /* assume some corect value... */ } csum_len = ptr - buf; if (csum_len > sizeof (buf)) error ("impossible"); /* paranoia */ len = ptr - (u_int8_t *) th; if (len & 0x03) error ("impossible"); /* as >>2 ... */ *lenp = htons (len); th->doff = len >> 2; *packet_len_p = len; return 0;}static void tcp_send_probe (probe *pb, int ttl) { int sk; int af = dest_addr.sa.sa_family; sockaddr_any addr; socklen_t len = sizeof (addr); /* To make sure we have chosen a free unused "source port", just create, (auto)bind and hold a socket while the port is needed. */ sk = socket (af, SOCK_STREAM, 0); if (sk < 0) error ("socket"); bind_socket (sk); if (getsockname (sk, &addr.sa, &len) < 0) error ("getsockname"); /* When we reach the target host, it can send us either RST or SYN+ACK. For RST all is OK (we and kernel just answer nothing), but for SYN+ACK we should reply with our RST. It is well-known "half-open technique", used by port scanners etc. This way we do not touch remote applications at all, unlike the ordinary connect(2) call. As the port-holding socket neither connect() nor listen(), it means "no such port yet" for remote ends, and kernel always send RST in such a situation automatically (we have to do nothing). */ th->source = addr.sin.sin_port; th->seq = random_seq (); th->check = 0; th->check = in_csum (buf, csum_len); if (ttl != last_ttl) { set_ttl (raw_sk, ttl); last_ttl = ttl; } pb->send_time = get_time (); if (do_send (raw_sk, th, th->doff << 2, &dest_addr) < 0) { close (sk); pb->send_time = 0; return; } pb->seq = th->source; pb->sk = sk; return;}static probe *tcp_check_reply (int sk, int err, sockaddr_any *from, char *buf, size_t len) { probe *pb; struct tcphdr *tcp = (struct tcphdr *) buf; u_int16_t sport, dport; if (len < 8) return NULL; /* too short */ if (err) { sport = tcp->source; dport = tcp->dest; } else { sport = tcp->dest; dport = tcp->source; } if (dport != dest_port) return NULL; if (!equal_addr (&dest_addr, from)) return NULL; pb = probe_by_seq (sport); if (!pb) return NULL; if (!err) pb->final = 1; return pb;}static void tcp_recv_probe (int sk, int revents) { if (!(revents & (POLLIN | POLLERR))) return; recv_reply (sk, !!(revents & POLLERR), tcp_check_reply);}static void tcp_expire_probe (probe *pb) { probe_done (pb);}static tr_module tcp_ops = { .name = "tcp", .init = tcp_init, .send_probe = tcp_send_probe, .recv_probe = tcp_recv_probe, .expire_probe = tcp_expire_probe, .user = 0, .options = tcp_options,};TR_MODULE (tcp_ops);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -