NF中调用dev_queue_xmit(skb)发送UDP包,对端没收到。
本帖最后由 mybname 于 2013-09-21 12:40 编辑在路由器上做开发,操作系统是openWRT,Linux内核版本3.10。
在NF_INET_POST_ROUTING点挂入勾子函数,勾子函数中检测DNS请求包。
检测到后,调用dev_alloc_skb()申请一个新skb,组装成UDP应答包,负载为一个字符串128字节。
UDP应答包中的Mac、IP、Port,按DNS请求包的源目的交换,最后调用dev_queue_xmit()函数发送。
测试:
PC ------ Route -------Internat
PC接路由器lan口,路由器wan口接外网。
PC上开抓包工具,ping www.baidu.com,
抓包工具显示正常的DNS请求和应答,没发现路由器发出的UDP应答包。
刚接触内核开发,在CU看了一些贴子,模仿着写了这么个东西,出了问题就不会调了。
尝试过不自己取设备指针,直接 新skb->dev = 旧skb->dev; PC仍未抓到包。
知道的有限, 请高手指点,非常感谢。
如果有相关的贴子,介绍一下也好。
附件中是可编译运行的代码。
路由器有四个端口,运行ifconfig看到:
br-lan 200.200.50.100:BB:CC:DD:EE:FF
eth0 记不清了
eht0.1 IP为空 00:BB:CC:DD:EE:FF
eth0.2 IP为空 00:BB:CC:DD:EE:FF
wan 192.168.4.1 00:01:02:03:04:05
--------------------------------------------------------
关键代码如下:
staticstruct nf_hook_opshttp_hooks[] =
{
{
.owner= THIS_MODULE,
.hook = ng_nf,
.pf = PF_INET,
.hooknum = NF_INET_POST_ROUTING,
.priority = NF_IP_PRI_FIRST,
},
};
static int ng_nf_init(void)
{
if(nf_register_hooks(http_hooks, ARRAY_SIZE(http_hooks)))
{
printk("%s %s %d nf_register_hooks failed\n", __FILE__,__func__,__LINE__);
}
return 0;
}
unsigned int ng_nf(unsigned int hooknum,
struct sk_buff *skb,
const struct net_device *in,
const struct net_device *out,
int(*okfn)(struct sk_buff *))
{
int iRet = netfilter_udp_send(skb);
return NF_ACCEPT;
}
//发送UDP包
unsigned int netfilter_udp_send(struct sk_buff *skb)
{
#define IF_NAME "eth0"
struct socket *sock;
char szBuff = {0};
char szPayLoad = {"--- Test UDP package ---"};
unsigned short uhDnsPort = htons(53);
unsigned short uhEthIpProto = htons(ETH_P_IP);
struct net_device *netdev = NULL;
struct net *net = NULL;
struct ethhdr *eth_header = NULL;
struct iphdr*ip_header= NULL;
struct udphdr *udp_header = NULL;
if(NULL == skb)
{
return 0;
}
//IP头
struct iphdr* pIpHdr = ip_hdr(skb);
if(pIpHdr->protocol != IPPROTO_UDP)//DNS是UDP包
{
return 0;
}
//UDP头
//struct udphdr* pUdpHdr = udp_hdr(skb);
struct udphdr* pUdpHdr = skb->data + pIpHdr->ihl*4;
if(pUdpHdr->dest != uhDnsPort)//DNS端口53
{
return 0;
}
//以太帧
struct ethhdr* pEthHdr = (struct ethhdr*)(skb->mac_header);
if(pEthHdr->h_proto != uhEthIpProto)
{
write_log("pEthHdr->h_proto != uhEthIpProto \n", pEthHdr->h_proto, uhEthIpProto);
return 0;
}
//显示收到的码流
printk("\n");
sprintfHex(szBuff, sizeof(szBuff), skb->mac_header, skb->data_len+14);
write_log("%s", szBuff);
//------------------------------------------
struct ifreq ifr;
sock_create_kern(PF_INET, SOCK_DGRAM, 0, &sock);
// copy the interface name to the ifrn_name.
strcpy(ifr.ifr_ifrn.ifrn_name, "eth0");
kernel_sock_ioctl(sock, SIOCSIFNAME, (unsigned long) &ifr);
net = sock_net((const struct sock *) sock->sk);
netdev = dev_get_by_name(net, "eth0");
int skb_len = LL_RESERVED_SPACE(netdev) + sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(szPayLoad);
struct sk_buff * nskb = dev_alloc_skb(skb_len);
if (NULL == nskb)
{
return 0;
}
//nskb->dev = skb->dev;//直接用原skb的设备也未收到包
nskb->dev = netdev;
nskb->pkt_type = PACKET_OTHERHOST;
nskb->protocol = htons(ETH_P_IP);
nskb->ip_summed = CHECKSUM_NONE;
nskb->priority= 0;
skb_reserve(nskb, LL_RESERVED_SPACE(netdev));
//ip头
skb_set_network_header(nskb, 0);
skb_put(nskb, sizeof(struct iphdr));
//udp头
skb_set_transport_header(nskb, sizeof(struct iphdr));
skb_put(nskb, sizeof(struct udphdr));
//--------------------------------------------
//construct udp header in skb
udp_header = udp_hdr(nskb);
udp_header->source = pUdpHdr->dest;
udp_header->dest = htons(9988); //pUdpHdr->source;
udp_header->len = htons(sizeof(struct udphdr) + sizeof(szPayLoad)); //htons(256);
udp_header->check = 0;
//construct ip header in skb
ip_header = ip_hdr(nskb);
ip_header->version = 4;
ip_header->ihl = sizeof(struct iphdr) >> 2;
ip_header->frag_off = 0;
ip_header->protocol = IPPROTO_UDP;
ip_header->tos = 0;
ip_header->daddr = pIpHdr->saddr;
ip_header->saddr = in_aton("200.200.50.1"); //pIpHdr->daddr;
ip_header->ttl = 0x40;
ip_header->tot_len = htons(sizeof(struct iphdr) + sizeof(struct udphdr) + sizeof(szPayLoad));
ip_header->check = 0;
// caculate checksum
ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);
nskb->csum = skb_checksum(nskb, ip_header->ihl*4, (sizeof(struct udphdr) + sizeof(szPayLoad)), 0);
udp_header->check = csum_tcpudp_magic(pIpHdr->daddr, pIpHdr->saddr, (sizeof(struct udphdr) + sizeof(szPayLoad)), IPPROTO_UDP, nskb->csum);
//copy data in skb
char *pData = skb_put(nskb, sizeof(szPayLoad));
if (NULL != pData)
{
memcpy(pData, szPayLoad, sizeof(szPayLoad));
//write_log("--payload: %s", szBuff);
}
//construct ethernet header in skb
eth_header = (struct ehthdr *)skb_push(nskb, 14);
memcpy(eth_header->h_dest, pEthHdr->h_source, ETH_ALEN);
memcpy(eth_header->h_source, pEthHdr->h_dest, ETH_ALEN);
eth_header->h_proto = htons(ETH_P_IP); //uhEthIpProto;
sprintfHex(szBuff, sizeof(szBuff), nskb->data-14, nskb->data_len+14);
// send packet
if ( dev_queue_xmit(nskb) < 0 )
{
dev_put(netdev);
kfree_skb(nskb);
printk("send packet by nskb failed.\n");
return 0;
}
write_log("--- send packet by nskb success. \n%s", szBuff);
return true;
}
可能计算校验和时,传的参数len有问题。
之前认为 len=UDP头长度 + 负载的长度。还要确认一下。
skb_checksum(const struct sk_buff *skb, int offset, int len, __wsum csum);
回复 2# mybname
在两边分别抓包看一下。另外可以参考本版精华帖合集http://bbs.chinaunix.net/thread-1930079-1-1.html
不知道你的checksum计算对不对,下面是我验证可行的方法,当个参考,你比较下。如果实在不行就把udp checksum置0吧,udp校验和是可选的。
static void update_chksum(struct iphdr *iph, struct udphdr *udp)
{
//ip
iph->check = 0;
iph->check = ip_fast_csum(iph,iph->ihl);
//udp
udp->check = 0;
udp->check = csum_tcpudp_magic(iph->saddr, iph->daddr,
ntohs(udp->len), IPPROTO_UDP,
csum_partial(udp, ntohs(udp->len), 0));
}
还有两点不太确定的,不知道对不对,说出来,楼主也考量下,如果说错了,请见谅。
1、看到你代码中在用udp_hdr(skb)取udp头部,这个似乎取不到吧,udp header是第四层的,现在才第三层,应该还没赋值,我记得是这样的。
2、你在做数据操作的时候,你确认你的skb是线性的? // caculate checksum
ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);
nskb->csum = skb_checksum(nskb, ip_header->ihl*4, (sizeof(struct udphdr) + sizeof(szPayLoad)), 0);
udp_header->check = csum_tcpudp_magic(pIpHdr->daddr, pIpHdr->saddr, (sizeof(struct udphdr) + sizeof(szPayLoad)), IPPROTO_UDP, nskb->csum);
调整一下顺序
skb->csum = skb_checksum (skb, iph->ihl*4, skb->len - iph->ihl * 4, 0);
udph->check = csum_tcpudp_magic (iph->saddr, iph->daddr,
skb->len - iph->ihl * 4, iph->protocol, skb->csum);
ip_header->check = ip_fast_csum(ip_header, ip_header->ihl);
感谢各位的答复,转眼2年时间,问题细节已模糊,大概做个交待。
本帖最后由 mybname 于 2015-04-20 23:44 编辑我的理解,调用dev_queue_xmit()函数发包,与校验和什么的没关系,随便给段码流,函数都会发出去。
但格式不对的话,对端网卡不收。抓包工具自然也抓不到。
关键要目地Mac正确,对端机器上抓包工具设置为抓所有的包,是可以抓到的。
1. 目地Mac对,源Mac错,对端网卡会收。抓包工具抓所有的包,可以抓到。
2. IP头校验和错误,内核解析失败抛弃。
只要对端能收到,配合抓包工具,哪里不对可以看出来了,慢慢调吧。 我现在也在学习内核刚好在做dns的内核回包,我是在prerouting 中做的,手动封装udp数据包,在postrouting 挂在钩子 监测 结果:没有发出去。我没有封eth头。使用的发送函数是ip_local_out。不知道您是否解决了最后。望回答:mail:532961704@qq.com
页:
[1]