skb学习记录-----sk_buff封装过程

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;
}

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值