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协议源码解析相关推荐

  1. 蓝牙hid协议源码解析

    1,概述 1.1 HID协议 HID协议: Hunman Interface Device Profile人机交互设备协议 使用场景:支持人机交互设备之间的控制 市场产品:蓝牙键盘,蓝牙鼠标,蓝牙游戏 ...

  2. 蓝牙map协议源码解析

    MAP协议 使用场景:智能车载中同步短信,彩信等信息 1 协议概述 协议代码路径: frameworks\opt\bluetooth\src\android\bluetooth\client\ map ...

  3. 代币标准--ERC1155协议源码解析

    ERC1155多代币标准 ERC1155结合了ERC20和ERC721的能力,这是一个标准接口,支持开发同质化的.半同质化的.非同质化的代币和其他配置的通用智能合约. IERC1155接口 // SP ...

  4. 代币标准--ERC721协议源码解析

    IERC165接口定义 interface IERC165 {function supportsInterface(bytes4 interfaceId) external view returns ...

  5. Dubbo源码解析-Dubbo服务消费者_Dubbo协议(一)

    前言: 在介绍完Dubbo 本地模式(Injvm协议)下的服务提供与消费后,上文我们又介绍了Dubbo远程模式(dubbo协议)下的服务暴露过程,本质上就是通过Netty将dubbo协议端口暴露出去, ...

  6. Thrift源码解析(二)序列化协议

    概述 对于一个RPC框架,定义好网络数据的序列化协议是最基本的工作,thrift的序列化协议主要包含如下几种: TBinaryProtocol TCompactProtocol TJSONProtoc ...

  7. ActiveMQ源码解析 建立连接

    作为一个消息中间件,有客户端和服务端两部分代码,这次的源码解析系列主要从客户端的代码入手,分成建立连接.消息发送.消息消费三个部分.趁着我昨天弄明白了源码编译的兴奋劲头还没过去,今天研究一下建立连接的 ...

  8. Cilium创建pod network源码解析

    01 Overview 我们生产K8s使用容器网络插件 Cilium 来创建 Pod network,下发 eBPF 程序实现 service 负载均衡来替换 kube-proxy,并且使用 BGP ...

  9. HDFS源码解析:教你用HDFS客户端写数据

    摘要:终于开始了这个很感兴趣但是一直觉得困难重重的源码解析工作,也算是一个好的开端. 本文分享自华为云社区<hdfs源码解析之客户端写数据>,作者: dayu_dls. 在我们客户端写数据 ...

  10. Linux-4.20.8内核桥收包源码解析(一)----------sk_buff(详细)

    作者:lwyang? 内核版本:Linux-4.20.8 网络子系统中用来存储数据的缓冲区叫做套接字缓存,简称SKB,可处理变长数据,尽量避免数据的复制. 每一个SKB都在设备中标识发送报文的目的或接 ...

最新文章

  1. 【Kaggle Learn】Python 5-8
  2. 好分数a1a5_好分数怎么查看班级排名 七年级学生成绩查询
  3. mysql 8.0创建远程连接用户
  4. matlab scatter cdata,matlab cdatamapping
  5. P1965 夜夜的数据加强 题解
  6. 转载 - 整数划分问题
  7. 这些行为,属于学术不端!
  8. php 获取子类的方法名,php获取分类下的所有子类方法
  9. ubuntu下安装配置java8
  10. java安装后怎么打开_java安装后怎么打开教程
  11. 单场淘汰制场次计算方法_校园足球联赛赛制的设计与编排
  12. vue js日期时间格式化
  13. 离散数学 之 命题公式的主析取合取范式(java实现)
  14. Android快捷开关实现
  15. 微信红包数字变化动态图片_微信红包数字动图下载_微信动态图片红包图下载_游戏吧...
  16. 计算机网络中的猫,什么是猫(调制解调器),猫有什么作用-电脑自学网
  17. ACE初学者使用指南
  18. 医美整形机构业务流程讲解
  19. Linux服务器配置与管理项目教程(CentOS7 /RHEL 7)(第三版)题库带答案
  20. 亚马逊SP-API申请 PII权限申请 ERP开发 开发人员注册

热门文章

  1. unity批量设置图片为etc2格式或者astc格式
  2. mysql及格率70以上_数据库实例(统计最高分学生信息,不及格率等等)
  3. android es2 es3,良心点评解析九号滑板车es2和es3有什么区别哪个好?老司机揭秘评测如何...
  4. 在Qt环境下进行人机交互界面设计--工具条
  5. 数据恢复——在Windows 10中恢复永久删除的文件的5种方法
  6. 如何在手机上新建html文件夹,用手机怎么制作网页
  7. 苹果手机来电归属地_手机号码归属地能否取消?
  8. 为什么别人可以打开的网站,你却打不开?原因找到了 ,HTTP ERROR 404
  9. 关于ProcessOn在线做图工具上找不到宋体,黑体字体样式的解答
  10. gcc/g++编译错误Assembler Error