packet协议源码解析
packet协议和raw协议都是操作系统给用户层提供的可以直接访问底层协议的接口,packet协议把raw更加底层,raw协议是由用户构造ip头和数据部分,mac层收到数据包的时候,先给ip层,ip层根据ip头中的协议字段分发给对应的raw套接字和对应的上层协议。packet协议是由用户构造mac头和数据部分,系统只负责发送和接收,mac头收到数据包的时候,根据mac头判断出上层协议,然后遍历packet_type链表,找出对应的协议,再把数据包分发给他处理。大致流程如下。
下面是packet协议的实现代码
/** INET An implementation of the TCP/IP protocol suite for the LINUX* operating system. INET is implemented using the BSD Socket* interface as the means of communication with the user level.** PACKET - implements raw packet sockets.** Version: @(#)packet.c 1.0.6 05/25/93** Authors: Ross Biro, <bir7@leland.Stanford.Edu>* Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>* Alan Cox, <gw4pts@gw4pts.ampr.org>** Fixes: * Alan Cox : verify_area() now used correctly* Alan Cox : new skbuff lists, look ma no backlogs!* Alan Cox : tidied skbuff lists.* Alan Cox : Now uses generic datagram routines I* added. Also fixed the peek/read crash* from all old Linux datagram code.* Alan Cox : Uses the improved datagram code.* Alan Cox : Added NULL's for socket options.* Alan Cox : Re-commented the code.* Alan Cox : Use new kernel side addressing* Rob Janssen : Correct MTU usage.* Dave Platt : Counter leaks caused by incorrect* interrupt locking and some slightly* dubious gcc output. Can you read* compiler: it said _VOLATILE_** This program is free software; you can redistribute it and/or* modify it under the terms of the GNU General Public License* as published by the Free Software Foundation; either version* 2 of the License, or (at your option) any later version.**/#include <linux/types.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/fcntl.h>
#include <linux/socket.h>
#include <linux/in.h>
#include <linux/inet.h>
#include <linux/netdevice.h>
#include "ip.h"
#include "protocol.h"
#include <linux/skbuff.h>
#include "sock.h"
#include <linux/errno.h>
#include <linux/timer.h>
#include <asm/system.h>
#include <asm/segment.h>/** We really ought to have a single public _inline_ min function!*/static unsigned long min(unsigned long a, unsigned long b)
{if (a < b) return(a);return(b);
}/** This should be the easiest of all, all we do is copy it into a buffer. */
// mac头接收到数据包时调用该函数
int packet_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
{struct sock *sk;unsigned long flags;/** When we registered the protocol we saved the socket in the data* field for just this event.*/// 见packet_init函数sk = (struct sock *) pt->data; /** The SOCK_PACKET socket receives _all_ frames, and as such * therefore needs to put the header back onto the buffer.* (it was removed by inet_bh()).*/skb->dev = dev;// 加上mac头的长度skb->len += dev->hard_header_len;/** Charge the memory to the socket. This is done specifically* to prevent sockets using all the memory up.*/// 接收缓冲区过大if (sk->rmem_alloc & 0xFF000000) {printk("packet_rcv: sk->rmem_alloc = %ld\n", sk->rmem_alloc);sk->rmem_alloc = 0;}// 读缓冲区满则丢包if (sk->rmem_alloc + skb->mem_len >= sk->rcvbuf) {
/* printk("packet_rcv: drop, %d+%d>%d\n", sk->rmem_alloc, skb->mem_len, sk->rcvbuf); */skb->sk = NULL;kfree_skb(skb, FREE_READ);return(0);}save_flags(flags);cli();skb->sk = sk;// 读缓冲区变小sk->rmem_alloc += skb->mem_len; /** Queue the packet up, and wake anyone waiting for it.*/// 挂载到socket的接收队列skb_queue_tail(&sk->receive_queue,skb);if(!sk->dead)sk->data_ready(sk,skb->len);restore_flags(flags);/** Processing complete.*/release_sock(sk); /* This is now effectively surplus in this layer */return(0);
}/** Output a raw packet to a device layer. This bypasses all the other* protocol layers and you must therefore supply it with a complete frame*/
// 用户提供mac头和数据
static int packet_sendto(struct sock *sk, unsigned char *from, int len,int noblock, unsigned flags, struct sockaddr_in *usin,int addr_len)
{struct sk_buff *skb;struct device *dev;struct sockaddr *saddr=(struct sockaddr *)usin;/** Check the flags. */if (flags) return(-EINVAL);/** Get and verify the address. */if (usin) {if (addr_len < sizeof(*saddr)) return(-EINVAL);} elsereturn(-EINVAL); /* SOCK_PACKET must be sent giving an address *//** Find the device first to size check it */saddr->sa_data[13] = 0;dev = dev_get(saddr->sa_data);if (dev == NULL) {return(-ENXIO);}/** You may not queue a frame bigger than the mtu. This is the lowest level* raw protocol and you must do your own fragmentation at this level.*/// mac头和数据的大小if(len>dev->mtu+dev->hard_header_len)return -EMSGSIZE;// 分配一个skb,消耗写缓冲区大小skb = sk->prot->wmalloc(sk, len, 0, GFP_KERNEL);/** If the write buffer is full, then tough. At this level the user gets to* deal with the problem - do your own algorithmic backoffs.*/if (skb == NULL) {return(-ENOBUFS);}/** Fill it in */skb->sk = sk;// 不需要缓存skb->free = 1;// 所有内容都由用户填充memcpy_fromfs(skb->data, from, len);skb->len = len;skb->arp = 1; /* No ARP needs doing on this (complete) frame *//** Now send it*/// 设备在运行则发送,否则销毁skbif (dev->flags & IFF_UP) dev_queue_xmit(skb, dev, sk->priority);elsekfree_skb(skb, FREE_WRITE);return(len);
}/** A write to a SOCK_PACKET can't actually do anything useful and will* always fail but we include it for completeness and future expansion.*/
// 该版本没用
static int packet_write(struct sock *sk, unsigned char *buff, int len, int noblock, unsigned flags)
{return(packet_sendto(sk, buff, len, noblock, flags, NULL, 0));
}/** Close a SOCK_PACKET socket. This is fairly simple. We immediately go* to 'closed' state and remove our protocol entry in the device list.* The release_sock() will destroy the socket if a user has closed the* file side of the object.*/static void packet_close(struct sock *sk, int timeout)
{sk->inuse = 1;sk->state = TCP_CLOSE;// 从链表中删除该socketdev_remove_pack((struct packet_type *)sk->pair);// 销毁packet_type结构kfree_s((void *)sk->pair, sizeof(struct packet_type));sk->pair = NULL;release_sock(sk);
}/** Create a packet of type SOCK_PACKET. We do one slightly irregular* thing here that wants tidying up. We borrow the 'pair' pointer in* the socket object so we can find the packet_type entry in the* device list. The reverse is easy as we use the data field of the* packet type to point to our socket.*/static int packet_init(struct sock *sk)
{struct packet_type *p;p = (struct packet_type *) kmalloc(sizeof(*p), GFP_KERNEL);if (p == NULL) return(-ENOMEM);// 设置接收函数p->func = packet_rcv;// num是用户传进来的protocolp->type = sk->num;p->data = (void *)sk;p->dev = NULL;dev_add_pack(p);/** We need to remember this somewhere. */// pcket_type和sock结构体互相关联,方便互找sk->pair = (struct sock *)p;return(0);
}/** Pull a packet from our receive queue and hand it to the user.* If necessary we block.*/int packet_recvfrom(struct sock *sk, unsigned char *to, int len,int noblock, unsigned flags, struct sockaddr_in *sin,int *addr_len)
{int copied=0;struct sk_buff *skb;struct sockaddr *saddr;int err;int truesize;saddr = (struct sockaddr *)sin;if (sk->shutdown & RCV_SHUTDOWN) return(0);/** If the address length field is there to be filled in, we fill* it in now.*/if (addr_len) *addr_len=sizeof(*saddr);/** Call the generic datagram receiver. This handles all sorts* of horrible races and re-entrancy so we can forget about it* in the protocol layers.*/skb=skb_recv_datagram(sk,flags,noblock,&err);/** An error occurred so return it. Because skb_recv_datagram() * handles the blocking we don't see and worry about blocking* retries.*/if(skb==NULL)return err;/** You lose any data beyond the buffer you gave. If it worries a* user program they can ask the device for its MTU anyway.*/// mac头+数据的大小truesize = skb->len;copied = min(len, truesize);// 复制到用户空间memcpy_tofs(to, skb->data, copied); /* We can't use skb_copy_datagram here *//** Copy the address. */if (saddr) {saddr->sa_family = skb->dev->type;memcpy(saddr->sa_data,skb->dev->name, 14);}/** Free or return the buffer as appropriate. Again this hides all the* races and re-entrancy issues from us.*/skb_free_datagram(skb);/** We are done.*/release_sock(sk);return(truesize);
}/** A packet read can succeed and is just the same as a recvfrom but without the* addresses being recorded.*/int packet_read(struct sock *sk, unsigned char *buff,int len, int noblock, unsigned flags)
{return(packet_recvfrom(sk, buff, len, noblock, flags, NULL, NULL));
}/** This structure declares to the lower layer socket subsystem currently* incorrectly embedded in the IP code how to behave. This interface needs* a lot of work and will change.*/struct proto packet_prot =
{sock_wmalloc,sock_rmalloc,sock_wfree,sock_rfree,sock_rspace,sock_wspace,packet_close,packet_read,packet_write,packet_sendto,packet_recvfrom,ip_build_header, /* Not actually used */NULL,NULL,ip_queue_xmit, /* These two are not actually used */NULL,NULL,NULL,NULL, datagram_select,NULL,packet_init,NULL,NULL, /* No set/get socket options */NULL,128,0,{NULL,},"PACKET",0, 0
};
packet协议源码解析相关推荐
- 蓝牙hid协议源码解析
1,概述 1.1 HID协议 HID协议: Hunman Interface Device Profile人机交互设备协议 使用场景:支持人机交互设备之间的控制 市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏 ...
- 蓝牙map协议源码解析
MAP协议 使用场景:智能车载中同步短信,彩信等信息 1 协议概述 协议代码路径: frameworks\opt\bluetooth\src\android\bluetooth\client\ map ...
- 代币标准--ERC1155协议源码解析
ERC1155多代币标准 ERC1155结合了ERC20和ERC721的能力,这是一个标准接口,支持开发同质化的.半同质化的.非同质化的代币和其他配置的通用智能合约. IERC1155接口 // SP ...
- 代币标准--ERC721协议源码解析
IERC165接口定义 interface IERC165 {function supportsInterface(bytes4 interfaceId) external view returns ...
- Dubbo源码解析-Dubbo服务消费者_Dubbo协议(一)
前言: 在介绍完Dubbo 本地模式(Injvm协议)下的服务提供与消费后,上文我们又介绍了Dubbo远程模式(dubbo协议)下的服务暴露过程,本质上就是通过Netty将dubbo协议端口暴露出去, ...
- Thrift源码解析(二)序列化协议
概述 对于一个RPC框架,定义好网络数据的序列化协议是最基本的工作,thrift的序列化协议主要包含如下几种: TBinaryProtocol TCompactProtocol TJSONProtoc ...
- ActiveMQ源码解析 建立连接
作为一个消息中间件,有客户端和服务端两部分代码,这次的源码解析系列主要从客户端的代码入手,分成建立连接.消息发送.消息消费三个部分.趁着我昨天弄明白了源码编译的兴奋劲头还没过去,今天研究一下建立连接的 ...
- Cilium创建pod network源码解析
01 Overview 我们生产K8s使用容器网络插件 Cilium 来创建 Pod network,下发 eBPF 程序实现 service 负载均衡来替换 kube-proxy,并且使用 BGP ...
- HDFS源码解析:教你用HDFS客户端写数据
摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...
- Linux-4.20.8内核桥收包源码解析(一)----------sk_buff(详细)
作者:lwyang? 内核版本:Linux-4.20.8 网络子系统中用来存储数据的缓冲区叫做套接字缓存,简称SKB,可处理变长数据,尽量避免数据的复制. 每一个SKB都在设备中标识发送报文的目的或接 ...
最新文章
- 【Kaggle Learn】Python 5-8
- 好分数a1a5_好分数怎么查看班级排名 七年级学生成绩查询
- mysql 8.0创建远程连接用户
- matlab scatter cdata,matlab cdatamapping
- P1965 夜夜的数据加强 题解
- 转载 - 整数划分问题
- 这些行为,属于学术不端!
- php 获取子类的方法名,php获取分类下的所有子类方法
- ubuntu下安装配置java8
- java安装后怎么打开_java安装后怎么打开教程
- 单场淘汰制场次计算方法_校园足球联赛赛制的设计与编排
- vue js日期时间格式化
- 离散数学 之 命题公式的主析取合取范式(java实现)
- Android快捷开关实现
- 微信红包数字变化动态图片_微信红包数字动图下载_微信动态图片红包图下载_游戏吧...
- 计算机网络中的猫,什么是猫(调制解调器),猫有什么作用-电脑自学网
- ACE初学者使用指南
- 医美整形机构业务流程讲解
- Linux服务器配置与管理项目教程(CentOS7 /RHEL 7)(第三版)题库带答案
- 亚马逊SP-API申请 PII权限申请 ERP开发 开发人员注册
热门文章
- unity批量设置图片为etc2格式或者astc格式
- mysql及格率70以上_数据库实例(统计最高分学生信息,不及格率等等)
- android es2 es3,良心点评解析九号滑板车es2和es3有什么区别哪个好?老司机揭秘评测如何...
- 在Qt环境下进行人机交互界面设计--工具条
- 数据恢复——在Windows 10中恢复永久删除的文件的5种方法
- 如何在手机上新建html文件夹,用手机怎么制作网页
- 苹果手机来电归属地_手机号码归属地能否取消?
- 为什么别人可以打开的网站,你却打不开?原因找到了 ,HTTP ERROR 404
- 关于ProcessOn在线做图工具上找不到宋体,黑体字体样式的解答
- gcc/g++编译错误Assembler Error