【发布时间】:2022-03-20 04:44:57
【问题描述】:
我正在尝试在 linux 系统 (ArchLinux) 上用 C 语言伪造原始 IP 和 UDP。为此,我使用了以下代码
char *src_ip = "192.168.10.1";
char *dst_ip = "192.168.30.3";
short src_port = 22221;
short dst_port = 11111;
struct pseudo_header
{
u_int32_t source_address;
u_int32_t dest_address;
u_int8_t placeholder;
u_int8_t protocol;
u_int16_t udp_length;
};
unsigned short csum(unsigned short *ptr,int nbytes)
{
register long sum;
unsigned short oddbyte;
register short answer;
sum=0;
while(nbytes>1) {
sum+=*ptr++;
nbytes-=2;
}
if(nbytes==1) {
oddbyte=0;
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
void fill_ip(struct iphdr *iph, int lendata /*including udp header*/)
{
iph->ihl = 5;
iph->version = 4;
iph->tos = 0;
iph->tot_len = sizeof (struct iphdr) + lendata; //sizeof (struct udphdr) + strlen(data);
iph->id = htonl (54321); //Id of this packet
iph->frag_off = 0;
iph->ttl = 255;
iph->protocol = IPPROTO_UDP;
iph->check = 0; //Set to 0 before calculating checksum
iph->saddr = inet_addr (src_ip); //Spoof the source ip address
iph->daddr = inet_addr (dst_ip);
iph->check = csum ((unsigned short *) iph, iph->tot_len);
}
void fill_udp(struct udphdr *udph, int lendata)
{
udph->source = htons (src_port);
udph->dest = htons (dst_port);
udph->len = htons(8 + lendata); //tcp header size
/* CSUM */
struct pseudo_header psh;
psh.source_address = inet_addr(src_ip);
psh.dest_address = inet_addr (dst_ip);
psh.placeholder = 0;
psh.protocol = IPPROTO_UDP;
psh.udp_length = htons(sizeof(struct udphdr) + lendata );
int psize = sizeof(struct pseudo_header) + sizeof(struct udphdr) + lendata;
char *pseudogram = malloc(psize);
memcpy(pseudogram , (char*) &psh , sizeof (struct pseudo_header));
memcpy(pseudogram + sizeof(struct pseudo_header) , udph , sizeof(struct udphdr) + lendata);
udph->check = csum( (unsigned short*) pseudogram , psize);
}
int main (int argc, char *argv[])
{
char datagram[4096]={0};
int s;
s = socket (AF_INET, SOCK_RAW, IPPROTO_RAW);
assert(s != -1);
//link to interface
const char *opt;
opt = "ens6";
const int len = strnlen(opt, IFNAMSIZ);
if (len == IFNAMSIZ) {
fprintf(stderr, "Too long iface name");
return 1;
}
assert(setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, opt, len) == 0);
// IP header
struct iphdr *iph = (struct iphdr *) datagram;
//UDP header
struct udphdr *udph = (struct udphdr *) (datagram + sizeof (struct ip));
// data
char *data = datagram + sizeof(struct iphdr) + sizeof(struct udphdr);
data[0] = 'a';
fill_ip(iph,sizeof(struct udphdr) + 1/*len of data*/);
fill_udp(udph,1);
struct sockaddr_in sin;
memset(&sin,0,sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(dst_port);
sin.sin_addr.s_addr = inet_addr (dst_ip);
sendto (s, datagram, iph->tot_len , 0, (struct sockaddr *) &sin, sizeof (sin));
return 0;
}
此代码创建一个从 192.168.10.1 到 192.168.30.3 的 UDP 数据包,源端口为 22221,目标端口为 11111。
然后,我添加一个路由规则并从主表中删除默认路由:
sudo ip rule add to 192.168.30.3 dport 11111 proto 17 table 2001
sudo ip route add default via 192.168.10.2 src 192.168.10.1 table 2001
sudo ip route del default
(主表中定义了192.168.10.2的路由)
这最终给出了以下规则:
# ip rule
0: from all lookup local
32765: from all to 192.168.30.3 dport 11111 lookup 2001 proto 17
32766: from all lookup main
32767: from all lookup default
还有以下路线:
# ip route show table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1
broadcast 192.168.10.0 dev ens6 proto kernel scope link src 192.168.10.1
local 192.168.10.1 dev ens6 proto kernel scope host src 192.168.10.1
broadcast 192.168.10.255 dev ens6 proto kernel scope link src 192.168.10.1
# ip route show table 2001
default via 192.168.10.2 dev ens6 src 192.168.10.1
192.168.10.0/24 dev ens6 proto kernel scope link src 192.168.10.1
# ip route show table main
192.168.10.0/24 dev ens6 proto kernel scope link src 192.168.10.1
我希望在 192.168.10.2 和 192.168.30.3 上看到带有wireshark 的原始数据包,但什么也没有。你知道为什么吗?也许我的原始套接字不遵循我定义的路由规则?请注意,如果我不触摸计算机上的路由规则,代码将按预期工作。
【问题讨论】:
-
你说得完全正确。你的里程会因操作系统而异(听起来你在 Linux 上)......但所有的赌注都在于期望通过“原始套接字”发送的手工制作的数据包遵守所有标准的 TCP/IP 级别行为(像“路线”)。看这里:squidarth.com/networking/systems/rc/2018/05/28/…
-
确实我在 Linux 上(我刚刚编辑了我的消息以包含此信息)。
-
据我了解,对于 Linux 上的原始套接字,只有目标地址用于路由。确实
sudo ip rule add to 192.168.30.3 table 2001有效,而sudo ip rule add to 192.168.30.3 dport 11111 proto 17 table 2001或sudo ip rule add from 192.168.10.1 to 192.168.30.3 table 2001无效
标签: c routes raw-sockets