- 浏览: 316006 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
JQ_AK47:
...
Linux下直接发送以太包 -
winsen2009:
谢谢分享,如果能再来一个列子就更好了,刚接触看完还是不懂的用
UNPv1_r3读书笔记: SCTP编程
本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn
6. 通知回调处理 6.1 初始化 在pf_key的初始化函数中定义了通知回调结构pfkeyv2_mgr: err = xfrm_register_km(&pfkeyv2_mgr); 该结构定义如下: static struct xfrm_mgr pfkeyv2_mgr = { .id = "pfkeyv2", .notify = pfkey_send_notify, .acquire = pfkey_send_acquire, .compile_policy = pfkey_compile_policy, .new_mapping = pfkey_send_new_mapping, .notify_policy = pfkey_send_policy_notify, }; 6.2 登记 /* net/xfrm/xfrm_state.c */ // 就是把xfrm_mgr结构挂接到xfrm_km_list链表 int xfrm_register_km(struct xfrm_mgr *km) { write_lock_bh(&xfrm_km_lock); // 将结构挂接到xfrm_km_list链表 list_add_tail(&km->list, &xfrm_km_list); write_unlock_bh(&xfrm_km_lock); return 0; } EXPORT_SYMBOL(xfrm_register_km); 6.3 发送通知 在pf_key中调用以下两个函数来调用通知回调函数, 分别针对安全策略操作和SA操作: // 安全策略通知回调 void km_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) { struct xfrm_mgr *km; read_lock(&xfrm_km_lock); // 状态的通知回调函数为notify_policy list_for_each_entry(km, &xfrm_km_list, list) if (km->notify_policy) km->notify_policy(xp, dir, c); read_unlock(&xfrm_km_lock); } // SA状态通知回调 void km_state_notify(struct xfrm_state *x, struct km_event *c) { struct xfrm_mgr *km; read_lock(&xfrm_km_lock); // 状态的通知回调函数为notify list_for_each_entry(km, &xfrm_km_list, list) if (km->notify) km->notify(x, c); read_unlock(&xfrm_km_lock); } 调用这些通知函数前要填写事件的相关参数, 如: static int pfkey_flush(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs) { unsigned proto; struct km_event c; proto = pfkey_satype2proto(hdr->sadb_msg_satype); if (proto == 0) return -EINVAL; xfrm_state_flush(proto); // 填写事件参数 // 协议 c.data.proto = proto; // 序列号 c.seq = hdr->sadb_msg_seq; // sock对方(用户空间进程)的pid c.pid = hdr->sadb_msg_pid; // 事件 c.event = XFRM_MSG_FLUSHSA; km_state_notify(NULL, &c); return 0; } 6.4 pf_key通知回调函数 6.4.1 发送SA消息时的通知回调 // 状态通知回调, 在SA操作后调用 static int pfkey_send_notify(struct xfrm_state *x, struct km_event *c) { // 根据事件类型来发送相关通知 switch (c->event) { case XFRM_MSG_EXPIRE: // SA到期的通知, SA消息类型为SADB_EXPIRE,只是进行了登记的PF_KEY socket可以收到 return key_notify_sa_expire(x, c); case XFRM_MSG_DELSA: case XFRM_MSG_NEWSA: case XFRM_MSG_UPDSA: // SA的通知, SA消息类型为分别为SADB_DELETE, SADB_ADD和SADB_UPDATE, // 所有PF_KEY socket都可以收到 return key_notify_sa(x, c); case XFRM_MSG_FLUSHSA: // 删除全部SA的通知, SA消息类型为分别为SADB_FLUSH, 所有PF_KEY socket都可以收到 return key_notify_sa_flush(c); case XFRM_MSG_NEWAE: /* not yet supported */ break; default: printk("pfkey: Unknown SA event %d\n", c->event); break; } return 0; } 6.4.2 发送ACQUIRE 关于ACQUIRE的作用, 摘一段UNP vol.1, r3中的话: chapter19.5: "When the kernel needs to communicate with a peer and policy says that an SA is required but one is not available, the kernel sends an SADB_ACQUIRE message to key management sockets that have registered the SA type required, containing a proposal extension describing the kernel's proposed algorithms and key lengths. The proposal may be a combination of what is supported by the system and preconfigured policy that limits what is permitted for this communication. The proposal is a list of algorithms, key lengths, and lifetimes, in order of preference. When a key management daemon receives an SADB_ACQUIRE message, it performs the acts required to choose a key that fits one of the kernel's proposed combinations, and installs this key in the kernel. " static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_policy *xp, int dir) { struct sk_buff *skb; struct sadb_msg *hdr; struct sadb_address *addr; struct sadb_x_policy *pol; struct sockaddr_in *sin; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct sockaddr_in6 *sin6; #endif int sockaddr_size; int size; struct sadb_x_sec_ctx *sec_ctx; struct xfrm_sec_ctx *xfrm_ctx; int ctx_size = 0; sockaddr_size = pfkey_sockaddr_size(x->props.family); if (!sockaddr_size) return -EINVAL; // 消息长度包括SA基本头, 两个SA地址, 两个网络地址和策略 size = sizeof(struct sadb_msg) + (sizeof(struct sadb_address) * 2) + (sockaddr_size * 2) + sizeof(struct sadb_x_policy); // 还添加AH或ESP中所有算法描述的长度 if (x->id.proto == IPPROTO_AH) size += count_ah_combs(t); else if (x->id.proto == IPPROTO_ESP) size += count_esp_combs(t); if ((xfrm_ctx = x->security)) { ctx_size = PFKEY_ALIGN8(xfrm_ctx->ctx_len); size += sizeof(struct sadb_x_sec_ctx) + ctx_size; } // 分配skb skb = alloc_skb(size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; // 先填基本SA消息头信息 hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg)); hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_type = SADB_ACQUIRE; hdr->sadb_msg_satype = pfkey_proto2satype(x->id.proto); hdr->sadb_msg_len = size / sizeof(uint64_t); hdr->sadb_msg_errno = 0; hdr->sadb_msg_reserved = 0; hdr->sadb_msg_seq = x->km.seq = get_acqseq(); hdr->sadb_msg_pid = 0; /* src address */ // 填充SA源地址信息 addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { // IPV4地址 addr->sadb_address_prefixlen = 32; sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; sin->sin_addr.s_addr = x->props.saddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { // IPV6地址 addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; sin6->sin6_flowinfo = 0; memcpy(&sin6->sin6_addr, x->props.saddr.a6, sizeof(struct in6_addr)); sin6->sin6_scope_id = 0; } #endif else BUG(); /* dst address */ // 填充SA目的地址信息 addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { addr->sadb_address_prefixlen = 32; sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; sin->sin_addr.s_addr = x->id.daddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; sin6->sin6_flowinfo = 0; memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct in6_addr)); sin6->sin6_scope_id = 0; } #endif else BUG(); // 填充策略信息 pol = (struct sadb_x_policy *) skb_put(skb, sizeof(struct sadb_x_policy)); pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t); pol->sadb_x_policy_exttype = SADB_X_EXT_POLICY; pol->sadb_x_policy_type = IPSEC_POLICY_IPSEC; pol->sadb_x_policy_dir = dir+1; pol->sadb_x_policy_id = xp->index; /* Set sadb_comb's. */ // 填充AH或ESP的可用算法信息 if (x->id.proto == IPPROTO_AH) dump_ah_combs(skb, t); else if (x->id.proto == IPPROTO_ESP) dump_esp_combs(skb, t); /* security context */ if (xfrm_ctx) { // 填充安全上下文信息 sec_ctx = (struct sadb_x_sec_ctx *) skb_put(skb, sizeof(struct sadb_x_sec_ctx) + ctx_size); sec_ctx->sadb_x_sec_len = (sizeof(struct sadb_x_sec_ctx) + ctx_size) / sizeof(uint64_t); sec_ctx->sadb_x_sec_exttype = SADB_X_EXT_SEC_CTX; sec_ctx->sadb_x_ctx_doi = xfrm_ctx->ctx_doi; sec_ctx->sadb_x_ctx_alg = xfrm_ctx->ctx_alg; sec_ctx->sadb_x_ctx_len = xfrm_ctx->ctx_len; memcpy(sec_ctx + 1, xfrm_ctx->ctx_str, xfrm_ctx->ctx_len); } // 广播到进行了登记的PF_KEY套接口 return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL); } 6.4.3 编译策略 // 将sadb_x_policy(标准接口格式)编译为xfrm_policy(内核具体实现格式) static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt, u8 *data, int len, int *dir) { struct xfrm_policy *xp; struct sadb_x_policy *pol = (struct sadb_x_policy*)data; struct sadb_x_sec_ctx *sec_ctx; // 选项opt必须是IP_IPSEC_POLICY, 否则出错 switch (sk->sk_family) { case AF_INET: if (opt != IP_IPSEC_POLICY) { *dir = -EOPNOTSUPP; return NULL; } break; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: if (opt != IPV6_IPSEC_POLICY) { *dir = -EOPNOTSUPP; return NULL; } break; #endif default: *dir = -EINVAL; return NULL; } *dir = -EINVAL; if (len < sizeof(struct sadb_x_policy) || pol->sadb_x_policy_len*8 > len || pol->sadb_x_policy_type > IPSEC_POLICY_BYPASS || (!pol->sadb_x_policy_dir || pol->sadb_x_policy_dir > IPSEC_DIR_OUTBOUND)) return NULL; // 分配策略空间 xp = xfrm_policy_alloc(GFP_ATOMIC); if (xp == NULL) { *dir = -ENOBUFS; return NULL; } // 填写策略动作 xp->action = (pol->sadb_x_policy_type == IPSEC_POLICY_DISCARD ? XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW); // 填写策略有效期参数 xp->lft.soft_byte_limit = XFRM_INF; xp->lft.hard_byte_limit = XFRM_INF; xp->lft.soft_packet_limit = XFRM_INF; xp->lft.hard_packet_limit = XFRM_INF; xp->family = sk->sk_family; xp->xfrm_nr = 0; // 解析ipsec请求 if (pol->sadb_x_policy_type == IPSEC_POLICY_IPSEC && (*dir = parse_ipsecrequests(xp, pol)) < 0) goto out; /* security context too */ if (len >= (pol->sadb_x_policy_len*8 + sizeof(struct sadb_x_sec_ctx))) { // 转换安全上下文 char *p = (char *)pol; struct xfrm_user_sec_ctx *uctx; p += pol->sadb_x_policy_len*8; sec_ctx = (struct sadb_x_sec_ctx *)p; if (len < pol->sadb_x_policy_len*8 + sec_ctx->sadb_x_sec_len) { *dir = -EINVAL; goto out; } if ((*dir = verify_sec_ctx_len(p))) goto out; uctx = pfkey_sadb2xfrm_user_sec_ctx(sec_ctx); *dir = security_xfrm_policy_alloc(xp, uctx); kfree(uctx); if (*dir) goto out; } *dir = pol->sadb_x_policy_dir-1; return xp; out: security_xfrm_policy_free(xp); kfree(xp); return NULL; } 6.4.4 NAT穿越映射 // 发送新映射(NAT穿越)SA消息, 包含SA头, SA, 转换前地址,端口, 转换后的地址端口 static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, u16 sport) { struct sk_buff *skb; struct sadb_msg *hdr; struct sadb_sa *sa; struct sadb_address *addr; struct sadb_x_nat_t_port *n_port; struct sockaddr_in *sin; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct sockaddr_in6 *sin6; #endif int sockaddr_size; int size; __u8 satype = (x->id.proto == IPPROTO_ESP ? SADB_SATYPE_ESP : 0); struct xfrm_encap_tmpl *natt = NULL; // 协议的地址长度 sockaddr_size = pfkey_sockaddr_size(x->props.family); if (!sockaddr_size) return -EINVAL; if (!satype) return -EINVAL; if (!x->encap) return -EINVAL; // NAT转换结构 natt = x->encap; /* Build an SADB_X_NAT_T_NEW_MAPPING message: * * HDR | SA | ADDRESS_SRC (old addr) | NAT_T_SPORT (old port) | * ADDRESS_DST (new addr) | NAT_T_DPORT (new port) */ // 消息总长: SA消息头+SA+两个SA地址+两个协议地址+2个NAT端口 size = sizeof(struct sadb_msg) + sizeof(struct sadb_sa) + (sizeof(struct sadb_address) * 2) + (sockaddr_size * 2) + (sizeof(struct sadb_x_nat_t_port) * 2); // 分配skb skb = alloc_skb(size + 16, GFP_ATOMIC); if (skb == NULL) return -ENOMEM; // 填写SA头 hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg)); hdr->sadb_msg_version = PF_KEY_V2; hdr->sadb_msg_type = SADB_X_NAT_T_NEW_MAPPING; hdr->sadb_msg_satype = satype; hdr->sadb_msg_len = size / sizeof(uint64_t); hdr->sadb_msg_errno = 0; hdr->sadb_msg_reserved = 0; hdr->sadb_msg_seq = x->km.seq = get_acqseq(); hdr->sadb_msg_pid = 0; /* SA */ // 填写SA结构 sa = (struct sadb_sa *) skb_put(skb, sizeof(struct sadb_sa)); sa->sadb_sa_len = sizeof(struct sadb_sa)/sizeof(uint64_t); sa->sadb_sa_exttype = SADB_EXT_SA; sa->sadb_sa_spi = x->id.spi; sa->sadb_sa_replay = 0; sa->sadb_sa_state = 0; sa->sadb_sa_auth = 0; sa->sadb_sa_encrypt = 0; sa->sadb_sa_flags = 0; /* ADDRESS_SRC (old addr) */ // 转换前SA地址参数 addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC; addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { // 填写转换前IPV4协议地址具体参数 addr->sadb_address_prefixlen = 32; sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; sin->sin_addr.s_addr = x->props.saddr.a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { // 填写转换前IPV6协议地址具体参数 addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; sin6->sin6_flowinfo = 0; memcpy(&sin6->sin6_addr, x->props.saddr.a6, sizeof(struct in6_addr)); sin6->sin6_scope_id = 0; } #endif else BUG(); /* NAT_T_SPORT (old port) */ // 填写转换前端口参数 n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port)); n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t); n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_SPORT; n_port->sadb_x_nat_t_port_port = natt->encap_sport; n_port->sadb_x_nat_t_port_reserved = 0; /* ADDRESS_DST (new addr) */ // 填写转换后地址属性参数 addr = (struct sadb_address*) skb_put(skb, sizeof(struct sadb_address)+sockaddr_size); addr->sadb_address_len = (sizeof(struct sadb_address)+sockaddr_size)/ sizeof(uint64_t); addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST; addr->sadb_address_proto = 0; addr->sadb_address_reserved = 0; if (x->props.family == AF_INET) { // 填写转换后IPV4协议地址具体参数 addr->sadb_address_prefixlen = 32; sin = (struct sockaddr_in *) (addr + 1); sin->sin_family = AF_INET; sin->sin_addr.s_addr = ipaddr->a4; sin->sin_port = 0; memset(sin->sin_zero, 0, sizeof(sin->sin_zero)); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) else if (x->props.family == AF_INET6) { // 填写转换后IPV6协议地址具体参数 addr->sadb_address_prefixlen = 128; sin6 = (struct sockaddr_in6 *) (addr + 1); sin6->sin6_family = AF_INET6; sin6->sin6_port = 0; sin6->sin6_flowinfo = 0; memcpy(&sin6->sin6_addr, &ipaddr->a6, sizeof(struct in6_addr)); sin6->sin6_scope_id = 0; } #endif else BUG(); /* NAT_T_DPORT (new port) */ // 填写转换前端口参数 n_port = (struct sadb_x_nat_t_port*) skb_put(skb, sizeof (*n_port)); n_port->sadb_x_nat_t_port_len = sizeof(*n_port)/sizeof(uint64_t); n_port->sadb_x_nat_t_port_exttype = SADB_X_EXT_NAT_T_DPORT; n_port->sadb_x_nat_t_port_port = sport; n_port->sadb_x_nat_t_port_reserved = 0; // 发送给进行登记了的PF_KEY套接口 return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL); } 6.4.5 发送策略的通知 // 策略通知回调, 在安全策略操作后调用 static int pfkey_send_policy_notify(struct xfrm_policy *xp, int dir, struct km_event *c) { if (xp && xp->type != XFRM_POLICY_TYPE_MAIN) return 0; switch (c->event) { case XFRM_MSG_POLEXPIRE: // 策略到期通知, 空函数 return key_notify_policy_expire(xp, c); case XFRM_MSG_DELPOLICY: case XFRM_MSG_NEWPOLICY: case XFRM_MSG_UPDPOLICY: // 策略的通知, SA消息类型为分别为SADB_X_SPDDELETE, SADB_X_SPDADD, SADB_X_SPDUPDATE等, // 所有PF_KEY socket都可以收到 return key_notify_policy(xp, dir, c); case XFRM_MSG_FLUSHPOLICY: if (c->data.type != XFRM_POLICY_TYPE_MAIN) break; // 策略的通知, SA消息类型为分别为SADB_X_FLUSH, 所有PF_KEY socket都可以收到 return key_notify_policy_flush(c); default: printk("pfkey: Unknown policy event %d\n", c->event); break; } return 0; } 在pf_key.c中只用到了6.4.1和6.4.5的这两个函数. ...... 待续 ......
发表评论
-
Linux内核中流量控制(24)
2011-01-10 16:33 2213本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(23)
2011-01-10 16:30 1497本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(22)
2011-01-10 16:29 1947本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(21)
2011-01-10 16:28 1362本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(20)
2011-01-10 16:27 1528本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(19)
2011-01-10 16:27 1985本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(18)
2011-01-10 16:26 1576Linux内核中流量控制(18) ... -
Linux内核中流量控制(17)
2011-01-10 16:25 1955本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(16)
2011-01-10 16:25 1812本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(15)
2011-01-10 16:24 1897本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(14)
2011-01-10 16:23 1965本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(13)
2011-01-10 16:22 2646本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(12)
2011-01-10 16:21 2115本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(11)
2011-01-10 16:21 3243本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(10)
2011-01-10 16:20 2012本文档的Copyleft归yfydz所 ... -
Linux内核中流量控制(9)
2011-01-10 16:19 1838本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(8)
2011-01-10 16:18 1504本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(7)
2011-01-10 16:18 2931本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(6)
2011-01-10 16:17 1500本文档的Copyleft归yfydz所有,使用GPL发布,可以 ... -
Linux内核中流量控制(5)
2011-01-10 16:16 1734本文档的Copyleft归yfydz所有,使用GPL发布,可以 ...
相关推荐
。。。
详细描述了linux2.4/2.6内核版本中的网络子系统。解释了协议的工作方式、建立了Linux网络体系结构中的多种重要概念——从设备驱动程序概念一直到应用程序接口的概念。能帮助读者更容易理解 Linux网络架构的进程和...
Linux内核之list_head,通常链表数据结构至少应包含两个域:数据域和指针域,数据域用于存储数据,指针域用于建立与下一个节点的联系。按照指针域的组织以及各个节点之间的联系形式,链表又可以分为单链表、双链表、...
linux内核源代码情景分析,对于深入学习者有很大帮助
笔记_Linux内核完全剖析_基于0.12内核
Linux网络体系结构 Linux内核中网络协议的设计与实现,Linux网络体系结构 Linux内核中网络协议的设计与实现
linux内核设计说明,Linux内核设计与实现(第三版中文高清带目录)
Linux内核完全注释:基于0.11内核(V5.0)
linux学习_Linux内核完全注释_Linux操作系统问价学习教程_怎么样读懂Linux内核,高清版,收集打包奉上。
Linux内核完全注释V3.0,很详细很不错,希望喜欢
linux内核设计与实现
编写本书是为了向学生和专业人员提供在Linux内核中实现网络功能时所需的基础知识,本书也适合所有希望深入理解操作系统内部网络特定进程的人。本书介绍了Linux内核的关键网络组件及机制,同时也介绍了通信系统的设计...
1.掌握TCP/IP协议栈的实现机理:学习Linux内核TCP/IP协议栈源代码可以帮助学生深入理解TCP/IP协议栈的实现机理,了解TCP/IP协议栈在Linux内核中的应用。 2.提高C程序设计能力:学习Linux内核TCP/IP协议栈源代码可以...
Linux 内核模块是 Linux 操作系统中的一种关键机制,它允许用户动态地修改内核、加载自己编写的程序,而不需要每次都编译内核。这种机制极大地改善了 Linux 的灵活性。 Linux 内核模块可以分为静态可加载模块和动态...
Linux内核map的一张详细关系图,可以打印出来作为学习内核的note使用,非常好,也很方便
Linux 内核更新包 wsl_update_x64 Linux 内核更新包 wsl_update_x64 Linux 内核更新包 wsl_update_x64 Linux 内核更新包 wsl_update_x64 Linux 内核更新包 wsl_update_x64 Linux 内核更新包 wsl_update_x64 Linux ...
Linux内核是一个整体是结构,因此向内核添加任何东西,或者删除某些功能,都十分困难。为了解决这个问题引入了内核机制。从而可以动态的想内核中添加或者删除模块。
linux内核软中断_linux内核源码详解.docx
linux内核版本历史_linux内核的作用.docx