Linux内核网络模块笔记 - IPv4查询路由与发出
背景
干网络这块的活,经常会涉及到一些报文封装与转发的需求.本文记录的就是完成报文的封装后,如何查询现有系统的路由来进行转发;
前提
IP层的信息已经就绪,意思是源IP(IP_SRC
),目的IP(IP_DST
);
涉及到的内核结构体
由于内核版本的差异,如下结构体可能会有所出入,大家灵活处理哈;
struct rtable
struct flowi
struct sk_buff
涉及到的内核函数
ip_route_output_key
ip_local_out
C代码:直接使用netfilter框架外发
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <net/route.h>
#include <net/flow.h>
extern int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
int cmpkt_out(struct sk_buff *skb) {
int ret = 0;
struct rtable *rp = NULL;
struct flowi fl4 = {
.u.ip4 = {},
};
//此处init_net,未考虑其他命名空间,可优化
rp = ip_route_output_key(&init_net, &fl4.u.ip4);
if (!rp)
goto drop;
skb->dev = rp->dst.dev;
skb_dst_set(skb, &rp->dst);
ret = ip_local_out(&init_net, skb->sk, skb);
end:
return ret;
drop:
ret = -2;
goto end;
}
C代码:自定义完成2层外发
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <net/route.h>
#include <net/flow.h>
extern int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
int cmpkt_l2_out(struct sk_buff *skb) {
int ret = 0;
struct rtable *rt = NULL;
__be32 nexthop;
struct neighbour *neigh;
struct ethhdr *eth;
struct iphdr *iph;
nexthop = 0;
rt = NULL;
neigh = NULL;
struct flowi fl4 = {
.u.ip4 = {},
};
//此处init_net,未考虑其他命名空间,可优化
rt = ip_route_output_key(&init_net, &fl4.u.ip4);
if (!rt)
goto drop;
skb->dev = rt->dst.dev;
skb_dst_set(skb, &rt->dst);
rcu_read_lock_bh();
nexthop = rt_nexthop(rt, iph->daddr);
neigh = __ipv4_neigh_lookup_noref(skb->dev, nexthop);
if (neigh == NULL || IS_ERR(neigh)) {
rcu_read_unlock_bh();
goto drop;
}
skb_reset_mac_header(skb);
memcpy(eth_hdr(skb)->h_dest, neigh->ha, ETH_ALEN);
memcpy(eth_hdr(skb)->h_source, skb->dev->dev_addr, ETH_ALEN);
//根据3层协议填值如下h_proto
eth_hdr(skb)->h_proto = htons(0x0800);
ret = dev_queue_xmit(skb);
rcu_read_unlock_bh();
return ret;
end:
return ret;
drop:
ret = -2;
goto end;
}