从ip_finish_output2到dev_queue_xmit路径:
arping命令:
arp协议:
arp报文结构:
(1).硬件类型:
硬件地址类型,该字段值一般为ARPHRD_ETHER,表示以太网。
F:\company\Linux\linux-4.1.45\linux-4.1.45\include\uapi\linux\if_arp.h
/* ARP protocol HARDWARE identifiers. */
#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
#define ARPHRD_EETHER 2 /* Experimental Ethernet */
#define ARPHRD_AX25 3 /* AX.25 Level 2 */
#define ARPHRD_PRONET 4 /* PROnet token ring */
#define ARPHRD_CHAOS 5 /* Chaosnet */
#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */
#define ARPHRD_ARCNET 7 /* ARCnet */
#define ARPHRD_APPLETLK 8 /* APPLEtalk */
#define ARPHRD_DLCI 15 /* Frame Relay DLCI */
#define ARPHRD_ATM 19 /* ATM */
...
(2).协议类型:
表示三层地址使用的协议,该字段值一般为ETH_P_IP,表示IP协议
F:\company\Linux\linux-4.1.45\linux-4.1.45\include\uapi\linux\if_ether.h
#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
#define ETH_P_PUP 0x0200 /* Xerox PUP packet */
#define ETH_P_PUPAT 0x0201 /* Xerox PUP Addr Trans packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
...
(3)硬件地址长度,以太网MAC地址就是6;
(4)协议地址长度,IP地址就是4;
(5)操作码
常见的有四种,arp请求,arp相应,rarp请求,rarp相应。
F:\company\Linux\linux-4.1.45\linux-4.1.45\include\uapi\linux\if_arp.h
/* ARP protocol opcodes. */
#define ARPOP_REQUEST 1 /* ARP request */
#define ARPOP_REPLY 2 /* ARP reply */
#define ARPOP_RREQUEST 3 /* RARP request */
#define ARPOP_RREPLY 4 /* RARP reply */
#define ARPOP_InREQUEST 8 /* InARP request */
#define ARPOP_InREPLY 9 /* InARP reply */
#define ARPOP_NAK 10 /* (ATM)ARP NAK */
(6)发送方硬件地址与IP地址,(7)目标硬件地址与目标IP地址。
arp头数据结构:
F:\company\Linux\linux-4.1.45\linux-4.1.45\include\uapi\linux\if_arp.h
/*
* This structure defines an ethernet arp header.
*/
struct arphdr {
__be16 ar_hrd; /* format of hardware address */
__be16 ar_pro; /* format of protocol address */
unsigned char ar_hln; /* length of hardware address */
unsigned char ar_pln; /* length of protocol address */
__be16 ar_op; /* ARP opcode (command) */
#if 0
/*
* Ethernet looks like this : This bit is variable sized however...
*/
unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
unsigned char ar_sip[4]; /* sender IP address */
unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
unsigned char ar_tip[4]; /* target IP address */
#endif
};
1. arp_init()
arp模块的初始化函数为arp_init(),这个函数在ipv4协议栈的初始化函数inet_init()中被调用。
1.初始化arp表arp_tbl;
2.注册arp协议类型;
3.建立arp相关proc文件,/proc/net/arp;
4.注册通知事件
F:\company\Linux\linux-4.1.45\linux-4.1.45\net\ipv4\arp.c
void __init arp_init(void)
{
neigh_table_init(NEIGH_ARP_TABLE, &arp_tbl);//初始化arp协议的邻居表
dev_add_pack(&arp_packet_type);//在协议栈中注册arp协议
arp_proc_init();//建立proc对象
#ifdef CONFIG_SYSCTL
neigh_sysctl_register(NULL, &arp_tbl.parms, NULL);
#endif
register_netdevice_notifier(&arp_netdev_notifier);//注册通知事件
}
arp邻居项函数指针表:
F:\company\Linux\linux-4.1.45\linux-4.1.45\net\ipv4\arp.c
static const struct neigh_ops arp_generic_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
.output = neigh_resolve_output,
.connected_output = neigh_connected_output,
};
static const struct neigh_ops arp_hh_ops = {
.family = AF_INET,
.solicit = arp_solicit,
.error_report = arp_error_report,
.output = neigh_resolve_output,
.connected_output = neigh_resolve_output,
};
static const struct neigh_ops arp_direct_ops = {
.family = AF_INET,
.output = neigh_direct_output,
.connected_output = neigh_direct_output,
};
neigh_table:
一个neigh_table对应一种邻居协议,IPv4就是arp协议。用来存储于邻居协议相关的参数、功能函数、邻居项散列表等。
struct neigh_table {
int family;/*地址族,arp为AF_INET*/
/*邻居项结构大小:sizeof(neighbour+4),因为neighbour结构最后一个成员0长数组,用于存储4字节长IP地址。*/
int entry_size;
/*hash函数所使用的键值长度,就是IP地址长度,为4*/
int key_len;
/*ETH_P_IP*/
__be16 protocol;
/*hash函数,arp_hash*/
__u32 (*hash)(const void *pkey,
const struct net_device *dev,
__u32 *hash_rnd);
bool (*key_eq)(const struct neighbour *, const void *pkey);
/*邻居表项初始化函数,用于初始化neighbour结构实例,即arp_constructor,在neigh_create中被调用*/
int (*constructor)(struct neighbour *);
/*创建和释放一个代理项时被调用,代理先不管*/
int (*pconstructor)(struct pneigh_entry *);
void (*pdestructor)(struct pneigh_entry *);
/*用来处理在proxy_queue缓存队列中的代理arp报文*/
void (*proxy_redo)(struct sk_buff *skb);
/*用来分配neighbour结构实例的缓存区名,即arp_cache。*/
char *id;
/*存储一些与协议相关的可调节参数,如超时重传时间,proxy_queue队列长度等*/
struct neigh_parms parms;
struct list_head parms_list;
int gc_interval;
int gc_thresh1;
int gc_thresh2;
int gc_thresh3;
unsigned long last_flush;
struct delayed_work gc_work;
/*处理proxy_queue的定时器*/
struct timer_list proxy_timer;
/*对于接收到的需要进行代理的arp报文,先缓存到proxy_queue,在定时器处理函数中再对其进行处理。*/
struct sk_buff_head proxy_queue;
/*邻居项条目数,在neigh_alloc()、neigh_destroy()中更新*/
atomic_t entries;
rwlock_t lock;
unsigned long last_rand;
/*记录邻居表中有关邻居项的各类统计数据*/
struct neigh_statistics __percpu *stats;
/*存储邻居项的散列表:hash表,用来存储邻居项*/
struct neigh_hash_table __rcu *nht;
/*存储arp代理三层协议地址的散列表*/
struct pneigh_entry **phash_buckets;
};
neighbour
一个neighbour对应一个邻居项,就是一个arp条目
struct neighbour {
struct neighbour __rcu *next;
struct neigh_table *tbl;/*指向arp_tbl*/
struct neigh_parms *parms;
unsigned long confirmed;
unsigned long updated;
rwlock_t lock;
atomic_t refcnt;/*引用计数*/
struct sk_buff_head arp_queue;/*用来缓存待发送的报文*/
unsigned int arp_queue_len_bytes;
struct timer_list timer;/*定时器*/
unsigned long used;
atomic_t probes;
__u8 flags;
__u8 nud_state;/*邻居项状态*/
__u8 type;/*邻居地址类型,例如单播、组播、广播等*/
/*生存标志,为1时,表示该邻居项正在被删除,最终通过垃圾回收将其删除*/
__u8 dead;
seqlock_t ha_lock;
/*邻居项MAC地址*/
unsigned char ha[ALIGN(MAX_ADDR_LEN, sizeof(unsigned long))];
/*缓存二层报头,包括目的MAC地址*/
struct hh_cache hh;
/*输出函数,用来将报文输出到该邻居*/
int (*output)(struct neighbour *, struct sk_buff *);
/*邻居项函数指针*/
const struct neigh_ops *ops;
struct rcu_head rcu;
struct net_device *dev;/*通过该设备访问邻居项*/
u8 primary_key[0];/*存储IP地址*/
};
邻居项函数指针表,实现三层和二层的dev_queue_xmit()之间的跳转。
struct neigh_ops {
int family;//AF_INET
/*发送arp报文*/
void (*solicit)(struct neighbour *, struct sk_buff *);
/*向三层报告错误*/
void (*error_report)(struct neighbour *, struct sk_buff *);
/*通用的输出函数,实现了完整的输出过程,存在较多的操作。*/
int (*output)(struct neighbour *, struct sk_buff *);
/*确定邻居可达,即状态为NUD_CONNETCTE时的输出函数,由于所有输出所需要的信息都已具备,
该函数只是简单地添加二层首部,发送*/
int (*connected_output)(struct neighbour *, struct sk_buff *);
};
neigh_statistics
用来存储统计信息,一个结构实例对应一个网络设备上的一种邻居协议。
struct neigh_statistics {
unsigned long allocs; /* number of allocated neighs */
unsigned long destroys; /* number of destroyed neighs */
unsigned long hash_grows; /* number of hash resizes */
unsigned long res_failed; /* number of failed resolutions */
unsigned long lookups; /* number of lookups */
unsigned long hits; /* number of hits (among lookups) */
unsigned long rcv_probes_mcast; /* number of received mcast ipv6 */
unsigned long rcv_probes_ucast; /* number of received ucast ipv6 */
unsigned long periodic_gc_runs; /* number of periodic GC runs */
unsigned long forced_gc_runs; /* number of forced GC runs */
unsigned long unres_discards; /* number of unresolved drops */
};
arp表结构:arp_tbl
F:\company\Linux\linux-4.1.45\linux-4.1.45\net\ipv4\arp.c
struct neigh_table arp_tbl = {
.family = AF_INET,
.key_len = 4,
.protocol = cpu_to_be16(ETH_P_IP),
.hash = arp_hash,
.key_eq = arp_key_eq,
.constructor = arp_constructor,
.proxy_redo = parp_redo,
.id = "arp_cache",
.parms = {
.tbl = &arp_tbl,
.reachable_time = 30 * HZ,
.data = {
[NEIGH_VAR_MCAST_PROBES] = 3,
[NEIGH_VAR_UCAST_PROBES] = 3,
[NEIGH_VAR_RETRANS_TIME] = 1 * HZ,
[NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ,
[NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ,
[NEIGH_VAR_GC_STALETIME] = 60 * HZ,
[NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024,
[NEIGH_VAR_PROXY_QLEN] = 64,
[NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ,
[NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10,
[NEIGH_VAR_LOCKTIME] = 1 * HZ,
},
},
.gc_interval = 30 * HZ,
.gc_thr