1、创建skb
struct sk_buff *skb = NULL;
skb = alloc_skb(500, GFP_ATOMIC);//第一个参数是要分配的数据段长度,即skb->data - skb->tail;下图是分配空间后相关指针的位置
2、初始定位
static inline void skb_reserve(struct sk_buff *skb, int len)
{
skb->data += len;
skb->tail += len;
}
该函数的作用是将skb->data往后偏移len,方便从应用层开始封装
例:skb_reserve(skb, 2+pkt_len + sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof(struct ethhdr));
加2是为了16字节对齐;
3、通过skb_push填充skb->data
unsigned char *skb_push(struct sk_buff *skb, unsigned int len)
{
skb->data -= len;
skb->len += len;
if (unlikely(skb->datahead))
skb_under_panic(skb, len, __builtin_return_address(0));
return skb->data;
}
调用该函数后,skb->data会向前偏移len,此时skb->data和skb->tail之间的空间可以用来填充相应的数据
4、设置相应的头部指针,在调用相应的skb->push后调用
a、网络层
static inline void skb_reset_network_header(struct sk_buff *skb)
{
skb->network_header = skb->data;
}
static inline void skb_reset_mac_header(struct sk_buff *skb)
{
skb->mac_header = skb->data;
}
b、传输层
static inline void skb_reset_transport_header(struct sk_buff *skb)
{
skb->transport_header = skb->data;
}
c、数据链路层
static inline void skb_reset_mac_header(struct sk_buff *skb)
{
skb->mac_header = skb->data;
}
例程:实现一个UDP数据包的发送
//构建一个udp报文并发送
int send_udp(struct net_bridge *br, u_char *smac, u_char *dmac,
u_char *pkt, int pkt_len, uint32_t sip, uint32_t dip,
uint16_t sport, uint16_t dport){
struct sk_buff *skb = NULL;
struct udphdr *udph = NULL;
struct iphdr *iph = NULL;
struct ethhdr *ethdr = NULL;
u_char *pdata = NULL;
int nret = 1;
if(NULL == smac || NULL == dmac)
goto out;
//创建一个skb
// skb = alloc_skb(2+pkt_len + sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof(struct ethhdr), GFP_ATOMIC);
skb = alloc_skb(500, GFP_ATOMIC);
if(NULL == skb)
goto out;
//为skb预留空间,方便后面skb_buff协议封装
skb_reserve(skb, 2+pkt_len + sizeof(struct udphdr) + sizeof(struct iphdr) + sizeof(struct ethhdr));
//skb字节填充
skb->dev = br->dev;
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = __constant_htons(ETH_P_IP);
skb->ip_summed = CHECKSUM_NONE;
skb->priority = 0;
//数据包封装
//分别压入应用层,传输层,网络层,链路层栈帧
//skb_push由后面往前面,与skb_put不同
pdata = skb_push(skb, pkt_len);
udph = (struct udphdr*)skb_push(skb, sizeof(struct udphdr));
skb_reset_transport_header(skb);
iph = (struct iphdr*)skb_push(skb, sizeof(struct iphdr));
skb_reset_network_header(skb);
ethdr = (struct ethhdr*)skb_push(skb, sizeof(struct ethhdr));
skb_reset_mac_header(skb);
//应用层数据填充
memcpy(pdata, pkt, pkt_len);
//传输层udp数据填充
memset(udph, 0, sizeof(struct udphdr));
udph->source = sport;
udph->dest = dport;
udph->len = htons(sizeof(struct udphdr) + pkt_len);//主机字节序转网络字节序
udph->check = 0;//skb_checksum之前必须置0.协议规定
//网络层数据填充
iph->version = 4;
iph->ihl = sizeof(struct iphdr) >> 2;
iph->frag_off = 0;
iph->protocol = IPPROTO_UDP;
iph->tos = 0;
iph->daddr = dip;
iph->saddr = sip;
iph->ttl = 0x40;
iph->tot_len = __constant_htons(pkt_len + sizeof(struct udphdr) + sizeof(struct iphdr));
iph->check = 0;
iph->check = ip_fast_csum((unsigned char*)iph, iph->ihl);//计算校验和
skb->csum = skb_checksum(skb, iph->ihl*4, skb->len - iph->ihl*4, 0);//skb校验和计算
udph->check = csum_tcpudp_magic(sip, dip, skb->len - iph->ihl*4, IPPROTO_UDP, skb->csum);//dup和tcp伪首部校验和
//链路层数据填充
memcpy(ethdr->h_dest, dmac, ETH_ALEN);
memcpy(ethdr->h_source, smac, ETH_ALEN);
ethdr->h_proto = __constant_htons(ETH_P_IP);
//skb_reset_mac_header(skb);
//调用内核协议栈函数,发送数据包
if(dev_queue_xmit(skb) < 0)
{
printk("dev_queue_xmit error\n");
goto out;
}
nret = 0;//这里是必须的
printk("dev_queue_xmit correct\n");
//出错处理
out:
if(0 != nret && NULL != skb)//这里前面的nret判断是必须的,不然必定死机
{
dev_put(br->dev);//减少设备的引用计数
kfree_skb(skb);//销毁数据包
}
return nret;//F_ACCEPT;
}