原文地址:http://wenx05124561.blog.163.com/blog/static/124000805201242023941402/
Linux内核中构造ip数据包,通过网卡驱动直接发送是一种有效的发送数据包方式。本文通过构造数据包和发送数据包来实现该方式。
- 构造数据包:构造vlant头的ip和udp数据包
#define IP_HEAD_LENTH 20
#define UDP_HEAD_LENTH 8
#define VLAN_ETH_HLENTH 18
#define VLAN_UDP_LENTH (VLAN_ETH_HLENTH + IP_HEAD_LENTH + UDP_HEAD_LENTH )
#define MTU_SIZE 1500
struct vlan_packet
{
unsigned char data[MTU_SIZE];
unsigned short length;
};
struct payload_info
{
unsigned char data[MTU_SIZE - VLAN_UDP_LENTH];
unsigned short length;
unsigned int daddr;
};
struct sk_buff *send_skb ;
int create_new_packet(struct vlan_packet *packet, struct payload_info * payload_info)
{
struct vlan_ethhdr *vethhdr;
struct iphdr *iph;
struct udphdr *uh;
struct gtp_u_hdr *gh;
unsigned char *payload;
//unsigned short udp_check;
struct net_device *dev;
struct sk_buff *skb = NULL;
if(payload_info->length + VLAN_UDP_LENTH > MTU_SIZE)
return -EINVAL;
//填充vlan_ethhdr头结构,即MAC数据
memset(packet->data, 0, MTU_SIZE);
vethhdr = (struct vlan_ethhdr*)packet->data;
memcpy (vethhdr->h_dest, dmac, ETH_ALEN);
memcpy (vethhdr->h_source, smac, ETH_ALEN);
vethhdr->h_vlan_proto = htons(ETH_P_8021Q); //vlan mac 头
vethhdr->h_vlan_TCI = htons(0x0);
vethhdr->h_vlan_encapsulated_proto = htons(0x0800);//下一层协议IP
packet->length = VLAN_ETH_HLEN;
//填充ip头数据
iph =(struct iphdr *)(packet->data + VLAN_ETH_HLEN);
iph->version = 4;
iph->ihl = 5;
iph->tos = 0;
iph->tot_len = htons(VLAN_UDP_LENTH - VLAN_ETH_HLEN + payload_info->length);
iph->id = 0;
iph->frag_off = htons(0x4000);
iph->ttl = 64;
iph->protocol = IPPROTO_UDP;//下一层udp数据包
dev = dev_get_by_name(&init_net, "eth0");
iph->daddr = payload_info->daddr;
iph->saddr = dev->ip_ptr->ifa_list->ifa_address;//获取网口eth0 IP address
iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);//ip 头校验和
packet->length += IP_HEAD_LENTH;
//填充udp头数据
uh = (struct udphdr *)(packet->data + VLAN_ETH_HLEN + IP_HEAD_LENTH);
uh->source = htons(0x1234);
uh->dest = htons(U_PORT);
uh->len = htons(UDP_HEAD_LENTH + payload_info->length);
packet->length += UDP_HEAD_LENTH;
//填充payload数据
payload = (packet->data + VLAN_ETH_HLEN + IP_HEAD_LENTH + UDP_HEAD_LENTH);
memcpy(payload, payload_info->data, payload_info->length);
packet->length += payload_info->length;
//分配skb数据结构
skb = dev_alloc_skb(packet->length + 200);
if (NULL == skb)
{
wx_debug("tmp skb alloc failed\n");
return -1;
}
skb_reserve(skb, 2);
skb_put(skb, packet->length);
skb->len = packet->length;
skb->protocol = htons(ETH_P_8021Q);
memcpy((unsigned char *)skb->data, packet->data, packet->length);
skb_pull(skb, VLAN_ETH_HLEN);
iph = (struct iphdr *)(skb->data);
skb_pull(skb, IP_HEAD_LENTH);
uh = (struct udphdr *)(skb->data);
//进行udp检验和计算
uh->check = 0;
skb->csum = csum_partial(skb->data, skb->len, 0);
uh->check = csum_tcpudp_magic(iph->saddr, iph->daddr, skb->len, iph->protocol, skb->csum);
skb_push(skb, IP_HEAD_LENTH + VLAN_ETH_HLEN);
skb->ip_summed = CHECKSUM_COMPLETE;
skb->dev=dev_get_by_name(&init_net, "eth0");
send_skb = skb; //将skb存储于变量send _skb
return 0;
}
- 发送数据包:通过复制存储send_skb, 利用发送函数进行发送。
int alloc_skb_send()
{
int ret = 0;
//复制数据包
struct sk_buff *skb = skb_clone(skb_send, GFP_KERNEL);
//发送数据包
ret = dev_queue_xmit(skb);
if(ret > 0 && skb != NULL)
{
kfree_skb(skb);
printk("dev_queue_xmit failed\n");
return -1;
}
return 0;
}