您的位置:首页 > 其它

内核udp报文截取、修改和发送

2016-01-15 14:04 225 查看
近来做一个产品需要在网关上获取特定UDP端口(假设是1000端口)的报文,并将其转发给其它设备的1000端口。虽然此类文章网上已经有很多了,但我还是贴上来,这样自己也做下记录,大家也多一份参考。
下面只给出了代码片段,自己慢慢调试。
我们假设网络拓扑如下所示:
LINUX 
--------- --------- --------- 
| PC-1 |-------| Server | -------| DataSrv | 
--------- --------- --------- 
pc-1发送udp报文到server的1000端口时,我们将报文拷贝一份并发送给DataSrv的1000端口。
报文的获取,我们采用netfilter hook. 我们将hook钩到 NF_IP_LOCAL_IN,优先级别设置成NF_IP_PRI_FIRST。
为什么hook到NF_IP_LOCAL_IN 而不是PERROUTING呢? 这是因为hook到 LOCAL_IN的话,我们就不用去考虑报文的重组了。如下图:
ip_rcv ------> ip_local_deliver ----------> netfilter 
|-----------ip_defrag-------^ 
为什么优先级别设置成最高了,这样可以防止udp的穿透。
这里我们不详细描述netfilter hook如何编写。下面我们来看获取到skb(重组好了的)的报文后,我们如何处理。
首先,我们假设DataSrv的ip地址为 192.168.1.254/24;Server的ip地址为 192.168.1.253/24。
#define TEST_XMIT(skb, rt) \
NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, (skb), NULL, \
(rt)->u.dst.dev, dst_output); 
#define IP_PARTS_NATIVE(n) \
(unsigned int)((n)&0xFF), \
(unsigned int)((n)>>8)&0xFF, \
(unsigned int)((n)>>16)&0xFF, \
(unsigned int)((n)>>24)&0xFF
#define TEST_BUG() BUG()
#define TEST_ERR(msg...) printk(KERN_ERR "TEST: " msg)
#define TEST_INFO(msg...) printk(KERN_INFO "TEST: " msg)
#define TEST_WARNING(msg...) printk(KERN_WARNING "TEST: " msg)
#define TEST_ERR_RL(msg...) \
do { \
if (net_ratelimit()) \
printk(KERN_ERR "TEST: " msg); \
} while (0)
//调用test_pop前 需要 拷贝或克隆 skb,然后再传入
static inline int TEST_pop (struct sk_buff * skb)
{
struct iphdr * iph = NULL;
struct rtable * rt = NULL;
struct ethhdr * ethh = NULL;
struct flowi fl;
unsigned long ulpeerip = 0;
unsigned int udphoff = 0;
fl.oif = 0;
fl.nl_u.ip4_u.daddr = in_aton ("192.168.1.254");
fl.nl_u.ip4_u.saddr = in_aton ("192.168.1.253");
fl.nl_u.ip4_u.tos = RT_TOS(0);
//查找出口路由
if (unlikely (ip_route_output_key(&rt, &fl))) {
TEST_ERR("%s no route from 192.168.1.253 to 192.168.1.254 (%s:%d)\n",
__FUNCTION__, __FILE__, __LINE__);
return (1);
}
//修改IP头
iph = skb->nh.iph;
iph->saddr = in_aton ("192.168.1.253");//sip;
iph->daddr = in_aton ("192.168.1.254");
//ip地址改变,需要重新计算udp校验和 
udph = (struct udphdr*) (skb->data iph->ihl * 4);
udphoff = iph->ihl * 4;
skb->csum = 0;
skb->csum = skb_checksum (skb, udphoff, skb->len - udphoff, 0);
udph->check = csum_tcpudp_magic (iph->saddr, iph->daddr,
skb->len - udphoff,
IPPROTO_UDP,
skb->csum);
//获取ip序号
ip_select_ident(iph, &rt->u.dst, NULL);
//重新计算ip头校验和
ip_send_check(iph);
//重新设置路有入口
dst_release(skb->dst);
skb->dst = &rt->u.dst;
//清除netfilter信息
nf_reset (skb);
//发送
TEST_XMIT (skb, rt);
return (0);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: