340 lines
12 KiB
C
340 lines
12 KiB
C
|
#include "router.h"
|
|||
|
#include "db.h"
|
|||
|
|
|||
|
int main(int argc, char const *argv[])
|
|||
|
{
|
|||
|
// 初始化
|
|||
|
net_init(); // 初始化网络和数据库
|
|||
|
|
|||
|
// pthread_t thread_net_task;
|
|||
|
// pthread_create(&thread_net_task, NULL, net_task, NULL);
|
|||
|
// pthread_join(thread_net_task, NULL);
|
|||
|
|
|||
|
close(sockfd); // 关闭原始套接字
|
|||
|
close_mysql(); // 关闭数据库连接
|
|||
|
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
void net_init()
|
|||
|
{
|
|||
|
// 创建原始套接字
|
|||
|
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); // 参数列表:协议族、套接字类型、协议类型
|
|||
|
if (sockfd < 0)
|
|||
|
{
|
|||
|
perror("socket");
|
|||
|
exit(-1);
|
|||
|
}
|
|||
|
|
|||
|
// 数据连接测试
|
|||
|
if (connect_mysql("localhost", 3306, "flykhan", "1202", "router") == 0)
|
|||
|
printf("数据库连接成功\n");
|
|||
|
else
|
|||
|
printf("数据库连接错误\n");
|
|||
|
|
|||
|
// 选择标志位
|
|||
|
int choice = 0;
|
|||
|
show(); // 打印菜单
|
|||
|
int pthread_flag = 0;
|
|||
|
while (1)
|
|||
|
{
|
|||
|
printf("请输入你的选择: \n");
|
|||
|
scanf("%d", &choice);
|
|||
|
getchar(); // 清空缓冲区
|
|||
|
if (1 == choice)
|
|||
|
{
|
|||
|
char add_blacked_ip[INET_ADDRSTRLEN] = "";
|
|||
|
printf("请输入要添加的黑名单IP:格式如 192.168.6.5\n");
|
|||
|
fgets(add_blacked_ip, sizeof(add_blacked_ip), stdin);
|
|||
|
add_blacked_ip[strlen(add_blacked_ip) - 1] = '\0'; // 将最后的换行符替换为字符串结束符
|
|||
|
printf("add_blacked_ip: %s\n", add_blacked_ip);
|
|||
|
// IP 格式检查
|
|||
|
if (is_ip_valid(add_blacked_ip) != 0)
|
|||
|
{
|
|||
|
printf("IP地址格式错误\n");
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
// 将IP地址加入黑名单
|
|||
|
MYSQL_BIND parmas[1];
|
|||
|
parmas[0].buffer_type = MYSQL_TYPE_STRING;
|
|||
|
parmas[0].buffer = add_blacked_ip;
|
|||
|
parmas[0].buffer_length = strlen(add_blacked_ip);
|
|||
|
parmas[0].is_null = 0;
|
|||
|
parmas[0].length = &parmas[0].buffer_length;
|
|||
|
const char *insert_ip_fw_sql = "insert into ip_fw(ip) values(?)";
|
|||
|
int result = insert(insert_ip_fw_sql, parmas);
|
|||
|
if (result > 0)
|
|||
|
{
|
|||
|
printf("黑名单添加成功\n");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("黑名单添加失败\n");
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
void *net_task(void *arg)
|
|||
|
{
|
|||
|
printf("net_task\n");
|
|||
|
|
|||
|
// 1. 获取可用的网络设备名称
|
|||
|
char errbuf[PCAP_ERRBUF_SIZE]; // 错误信息
|
|||
|
pcap_if_t *alldevs;
|
|||
|
if (pcap_findalldevs(&alldevs, errbuf) == -1)
|
|||
|
{
|
|||
|
printf("pcap_findalldevs error: %s\n", errbuf);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// 2. 打印网络设备名称
|
|||
|
pcap_if_t *d;
|
|||
|
for (d = alldevs; d != NULL; d = d->next)
|
|||
|
{
|
|||
|
printf("%s\n", d->name);
|
|||
|
}
|
|||
|
|
|||
|
// 3. 获取第一个网络设备的IP地址
|
|||
|
pcap_addr_t *a;
|
|||
|
for (a = alldevs->addresses; a != NULL; a = a->next)
|
|||
|
{
|
|||
|
if (a->addr->sa_family == AF_INET)
|
|||
|
{
|
|||
|
// ntoa 将网络字节序的IP地址转换为点分十进制的字符串
|
|||
|
printf("%s\n", inet_ntoa(((struct sockaddr_in *)a->addr)->sin_addr));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 获取网卡的网络号和网络掩码
|
|||
|
bpf_u_int32 netip, netmask;
|
|||
|
if (pcap_lookupnet(alldevs->name, &netip, &netmask, NULL) != 0)
|
|||
|
{
|
|||
|
printf("网络号和网络掩码获取错误\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
unsigned char ip[INET_ADDRSTRLEN] = "";
|
|||
|
unsigned char mask[INET_ADDRSTRLEN] = "";
|
|||
|
// ntop 将网络字节序的IP地址转换为点分十进制的字符串
|
|||
|
inet_ntop(AF_INET, &netip, ip, INET_ADDRSTRLEN);
|
|||
|
inet_ntop(AF_INET, &netmask, mask, INET_ADDRSTRLEN);
|
|||
|
printf("网络号: %s 网络掩码: %s\n", ip, mask);
|
|||
|
|
|||
|
// 获取网卡的MAC地址
|
|||
|
struct ifreq ifr;
|
|||
|
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
|||
|
strcpy(ifr.ifr_name, alldevs->name);
|
|||
|
if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) < 0)
|
|||
|
{
|
|||
|
printf("MAC地址获取失败\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
unsigned char mac[6];
|
|||
|
memcpy(mac, ifr.ifr_hwaddr.sa_data, 6);
|
|||
|
printf("MAC地址: %02x:%02x:%02x:%02x:%02x:%02x\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
|
|||
|
|
|||
|
// 打开网络设备,开始抓包(捕获数据包)
|
|||
|
pcap_t *pcap_handle = pcap_open_live(alldevs->name, 65535, 1, 0, errbuf); // 65535 最大捕获的字节数 1 混杂模式 0 不超时 errbuf 错误信息
|
|||
|
if (pcap_handle == NULL)
|
|||
|
{
|
|||
|
printf("pcap_open_live error: %s\n", errbuf);
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// 4. 抓包
|
|||
|
struct pcap_pkthdr pcap_header; // 数据包头
|
|||
|
bzero(&pcap_header, sizeof(pcap_header)); // 清空
|
|||
|
const u_char *packet; // 数据包数据
|
|||
|
while (1)
|
|||
|
{
|
|||
|
packet = pcap_next(pcap_handle, &pcap_header); // 抓包
|
|||
|
if (packet == NULL)
|
|||
|
{
|
|||
|
printf("pcap_next error\n");
|
|||
|
return 1;
|
|||
|
}
|
|||
|
printf("数据包长度: %d\n", pcap_header.len);
|
|||
|
printf("捕获的数据长度: %d\n", pcap_header.caplen);
|
|||
|
// printf("数据包时间: %s", ctime((const time_t *)&pcap_header.ts.tv_sec));
|
|||
|
// printf("数据包内容: ");
|
|||
|
// for (int i = 0; i < pcap_header.len; i++)
|
|||
|
// {
|
|||
|
// printf("%02x ", packet[i]);
|
|||
|
// }
|
|||
|
|
|||
|
// 5. 解析数据包
|
|||
|
struct ether_header *eth_header = (struct ether_header *)packet; // 以太网帧头
|
|||
|
unsigned char src_mac[18] = "";
|
|||
|
unsigned char dst_mac[18] = "";
|
|||
|
sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x", eth_header->ether_shost[0], eth_header->ether_shost[1], eth_header->ether_shost[2], eth_header->ether_shost[3], eth_header->ether_shost[4], eth_header->ether_shost[5]); // 将MAC地址转换为点分十进制的字符串
|
|||
|
sprintf(dst_mac, "%02x:%02x:%02x:%02x:%02x:%02x", eth_header->ether_dhost[0], eth_header->ether_dhost[1], eth_header->ether_dhost[2], eth_header->ether_dhost[3], eth_header->ether_dhost[4], eth_header->ether_dhost[5]);
|
|||
|
unsigned short eth_type = ntohs(eth_header->ether_type); // type 字段是网络字节序,需要转换为主机字节序
|
|||
|
printf("以太网帧类型: %04x\n", eth_type); // 0x080 IP协议 0x0806 ARP协议 0x8035 RARP协议 0x86dd IPv6协议
|
|||
|
printf("目的MAC地址: %s\n", dst_mac);
|
|||
|
printf("源MAC地址: %s\n", src_mac);
|
|||
|
|
|||
|
// 如果是ARP协议
|
|||
|
if (eth_type == 0x0806)
|
|||
|
{
|
|||
|
// // 分析 arp 报文
|
|||
|
// struct ether_arp *arp_header = (struct ether_arp *)(packet + sizeof(struct ether_header)); // arp 报文
|
|||
|
// unsigned short this_arp_op = ntohs(arp_header->arp_op); // 操作码
|
|||
|
// printf("ARP操作码: %d\n", this_arp_op); // 1 ARP请求 2 ARP应答 3 RARP请求 4 RARP应答
|
|||
|
// if (this_arp_op == 1 || this_arp_op == 2)
|
|||
|
// {
|
|||
|
// unsigned char src_ip[INET_ADDRSTRLEN] = "";
|
|||
|
// unsigned char dst_ip[INET_ADDRSTRLEN] = "";
|
|||
|
// inet_ntop(AF_INET, arp_header->arp_spa, src_ip, INET_ADDRSTRLEN); // 将网络字节序的IP地址转换为点分十进制的字符串
|
|||
|
// inet_ntop(AF_INET, arp_header->arp_tpa, dst_ip, INET_ADDRSTRLEN);
|
|||
|
// printf("源IP地址: %s\n", src_ip);
|
|||
|
// printf("目的IP地址: %s\n", dst_ip);
|
|||
|
// }
|
|||
|
|
|||
|
// 发送 ARP 请求,用于广播获取网段内的所有已连接设备的mac地址,并存入 ip_mac 映射表
|
|||
|
}
|
|||
|
|
|||
|
// 如果是IP协议
|
|||
|
if (eth_type == 0x800)
|
|||
|
{
|
|||
|
// 分析 ip 报文
|
|||
|
struct iphdr *ip_header = (struct iphdr *)(packet + sizeof(struct ether_header)); // ip 报文 (sizeof(struct ether_header) = 14 (MAC)以太网帧头长度)
|
|||
|
unsigned char src_ip[INET_ADDRSTRLEN] = "";
|
|||
|
unsigned char dst_ip[INET_ADDRSTRLEN] = "";
|
|||
|
unsigned char ip_protocol[INET_ADDRSTRLEN] = ""; // 协议类型
|
|||
|
inet_ntop(AF_INET, &ip_header->saddr, src_ip, INET_ADDRSTRLEN); // 将网络字节序的IP地址转换为点分十进制的字符串
|
|||
|
inet_ntop(AF_INET, &ip_header->daddr, dst_ip, INET_ADDRSTRLEN);
|
|||
|
printf("IP协议类型: %d\n", ip_header->protocol); // ICMP(1),IGMP(2),TCP(6),UDP(17),IPv6(41)
|
|||
|
printf("源IP地址: %s\n", src_ip);
|
|||
|
printf("目的IP地址: %s\n", dst_ip);
|
|||
|
|
|||
|
// 如果是黑名单IP,则跳过本次循环,即屏蔽本ip的数据
|
|||
|
if (is_blocked_ip(src_ip) == 0 || is_blocked_ip(dst_ip) == 0)
|
|||
|
{
|
|||
|
printf("数据报已被过滤\n");
|
|||
|
continue;
|
|||
|
}
|
|||
|
// is_blocked_ip(src_ip);
|
|||
|
printf("数据报正常\n");
|
|||
|
|
|||
|
// 不在黑名单时,进行数据报中ip和mac地址的转换和转发
|
|||
|
|
|||
|
// 先检查ip对应的mac地址是否存在(mysql->ip_mac表查询),存在时进行下一步;不存在时,调用三次循环,来进行ARP广播,获取最新的设备ip--mac
|
|||
|
}
|
|||
|
|
|||
|
printf("\n");
|
|||
|
sleep(1);
|
|||
|
}
|
|||
|
|
|||
|
// 3. 释放资源
|
|||
|
pcap_close(pcap_handle); // 关闭网络设备
|
|||
|
pcap_freealldevs(alldevs); // 释放资源
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
// MySQL 测试
|
|||
|
int is_blocked_ip(unsigned char *ip)
|
|||
|
{
|
|||
|
char sql[1024] = "";
|
|||
|
sprintf(sql, "select * from ip_fw where ip = '%s'", ip);
|
|||
|
int rows = result_rows(sql);
|
|||
|
|
|||
|
printf("打印 %s\n", sql);
|
|||
|
printf("rows: %d\n", rows);
|
|||
|
|
|||
|
// execlp("iptables", "iptables", "-A", "FORWARD", "-s", ip, "-j", "DROP", NULL); // 将源IP地址加入防火墙
|
|||
|
execlp("iptables", "iptables", "-D", "FORWARD", "-s", ip, "-j", "DROP", NULL); // 将源IP地址加入防火墙
|
|||
|
|
|||
|
// 获取行数
|
|||
|
if (rows > 0)
|
|||
|
{
|
|||
|
printf("IP地址 %s 在黑名单中\n", ip);
|
|||
|
// execlp("iptables", "iptables", "-A", "FORWARD", "-s", ip, "-j", "DROP", NULL); // 将源IP地址加入防火墙
|
|||
|
// 从防火墙删除
|
|||
|
execlp("iptables", "iptables", "-D", "FORWARD", "-s", ip, "-j", "DROP", NULL); // 将源IP地址加入防火墙
|
|||
|
return 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("IP地址 %s 不在黑名单中\n", ip);
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
int printResult(MYSQL_ROW row, char (*columns)[30], int cols)
|
|||
|
{
|
|||
|
printf("printResult\n");
|
|||
|
printf("cols: %d\n", cols);
|
|||
|
printf("row: %s\n", row[0]);
|
|||
|
for (int i = 0; i < cols; i++)
|
|||
|
{
|
|||
|
printf("%s: %s\n", columns[i], row[i]);
|
|||
|
}
|
|||
|
printf("\n");
|
|||
|
|
|||
|
int rows = atoi(row[0]);
|
|||
|
if (rows > 0)
|
|||
|
{
|
|||
|
printf("IP地址 在黑名单中\n");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("IP地址不在黑名单中\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
return atoi(row[0]); // 返回行数
|
|||
|
}
|
|||
|
|
|||
|
void show()
|
|||
|
{
|
|||
|
printf("************************************************\n");
|
|||
|
printf("**********路由器黑名单管理系统******************\n");
|
|||
|
printf("**************1.添加黑名单IP********************\n");
|
|||
|
printf("**************2.显示所有黑名单******************\n");
|
|||
|
printf("**************3.删除黑名单**********************\n");
|
|||
|
printf("**************4.开启路由器**********************\n");
|
|||
|
printf("**************5.刷新ARP表缓存*******************\n");
|
|||
|
printf("**************6.显示所有ARP表缓存***************\n");
|
|||
|
printf("**************9.重新打印菜单********************\n");
|
|||
|
printf("**************0.退出此系统**********************\n");
|
|||
|
printf("************************************************\n");
|
|||
|
}
|
|||
|
|
|||
|
// 判断IP地址格式是否正确
|
|||
|
int is_ip_valid(const char *ip)
|
|||
|
{
|
|||
|
regex_t regex; // 正则表达式
|
|||
|
int ret;
|
|||
|
|
|||
|
// 编译正则表达式
|
|||
|
// /^((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.){3}(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])(?::(?:[0-9]|[1-9][0-9]{1,3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5]))?$/
|
|||
|
ret = regcomp(®ex, "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", REG_EXTENDED); // REG_EXTENDED 扩展正则表达式
|
|||
|
if (ret != 0)
|
|||
|
{
|
|||
|
printf("regcomp error\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
|
|||
|
// 执行正则表达式
|
|||
|
ret = regexec(®ex, ip, 0, NULL, 0);
|
|||
|
regfree(®ex); // 释放正则表达式
|
|||
|
|
|||
|
if (ret == 0)
|
|||
|
{
|
|||
|
printf("IP地址格式正确\n");
|
|||
|
return 0;
|
|||
|
}
|
|||
|
else if (ret == REG_NOMATCH)
|
|||
|
{
|
|||
|
printf("IP地址格式非法\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
printf("正则表达式匹配错误\n");
|
|||
|
return -1;
|
|||
|
}
|
|||
|
}
|