




已阅读5页,还剩32页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Linux内核中的IPSEC实现(2)本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。msn: yfydz_来源:4. 状态(xfrm_state)处理本节所介绍的函数都在net/xfrm/xfrm_state.c中定义。4.1 状态分配状态分配函数为xfrm_state_alloc(), 该函数被pfkey_msg2xfrm_state()函数调用, pfkey_msg2xfrm_state()函数是将标准的pfkey_msg(SA结构)转换为xfrm状态, 同时该函数也被其他状态处理函数调用.struct xfrm_state *xfrm_state_alloc(void)struct xfrm_state *x;/ 分配空间x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);if (x) / 使用数初始化为1atomic_set(&x-refcnt, 1);/ 被0个ipsec通道使用atomic_set(&x-tunnel_users, 0);/ 初始化链表节点, 状态可按目的地址, 源地址和SPI挂接到不同链表INIT_HLIST_NODE(&x-bydst);INIT_HLIST_NODE(&x-bysrc);INIT_HLIST_NODE(&x-byspi);/ 状态定时器init_timer(&x-timer);/ 定时器处理函数x-timer.function = xfrm_timer_handler;x-timer.data = (unsigned long)x;/ 回放检测定时器init_timer(&x-rtimer);/ 回放定时器处理函数x-rtimer.function = xfrm_replay_timer_handler;x-rtimer.data = (unsigned long)x;x-curlft.add_time = (unsigned long)xtime.tv_sec;/ SA生命期参数x-lft.soft_byte_limit = XFRM_INF;x-lft.soft_packet_limit = XFRM_INF;x-lft.hard_byte_limit = XFRM_INF;x-lft.hard_packet_limit = XFRM_INF;/ 回放处理参数x-replay_maxage = 0;x-replay_maxdiff = 0;/ 初始化状态锁spin_lock_init(&x-lock);return x;EXPORT_SYMBOL(xfrm_state_alloc); / 状态定时器超时处理函数static void xfrm_timer_handler(unsigned long data)struct xfrm_state *x = (struct xfrm_state*)data;unsigned long now = (unsigned long)xtime.tv_sec;long next = LONG_MAX;int warn = 0;spin_lock(&x-lock);/ 如果该xfrm状态已经处于死亡状态, 可以返回了if (x-km.state = XFRM_STATE_DEAD)goto out;/ 如果处于生命期到期状态, 转到期处理if (x-km.state = XFRM_STATE_EXPIRED)goto expired;/ 如果到期了还要强制要增加一些时间if (x-lft.hard_add_expires_seconds) / 计算强制增加的超时时间long tmo = x-lft.hard_add_expires_seconds +x-curlft.add_time - now;/ 没法增加超时了, 到期if (tmo = 0)goto expired;if (tmo lft.hard_use_expires_seconds) / 计算强制增加的使用时间long tmo = x-lft.hard_use_expires_seconds +(x-curlft.use_time ? : now) - now;/ 没法增加超时了, 到期if (tmo = 0)goto expired;if (tmo km.dying)goto resched;/ 如果到期了还要软性要增加一些时间if (x-lft.soft_add_expires_seconds) / 计算软性增加的时间long tmo = x-lft.soft_add_expires_seconds +x-curlft.add_time - now;/ 软性增加超时不可用了if (tmo = 0)warn = 1;else if (tmo lft.soft_use_expires_seconds) / 计算软性增加的使用时间long tmo = x-lft.soft_use_expires_seconds +(x-curlft.use_time ? : now) - now;/ 软性增加超时不可用了if (tmo = 0)warn = 1;else if (tmo km.dying = warn;/ 软性增加超时已比不可用, 进行状态的超时到期通知if (warn)km_state_expired(x, 0, 0);resched:/ 如果增加的超时有效, 修改定时器超时时间if (next != LONG_MAX)mod_timer(&x-timer, jiffies + make_jiffies(next);goto out;expired:/ 状态到期if (x-km.state = XFRM_STATE_ACQ & x-id.spi = 0) / 如果这个状态是ACQ类型状态(不是用户空间主动建立的状态,而是内核根据策略主动要求/ 用户空间进行IKE协商建立的状态)/ 状态设置为到期x-km.state = XFRM_STATE_EXPIRED;/ 唤醒等待队列准备进行垃圾搜集操作wake_up(&km_waitq);next = 2;goto resched;/ 删除状态, 进行状态的到期通知if (!_xfrm_state_delete(x) & x-id.spi)/ 1表示是硬性到期了km_state_expired(x, 1, 0);out:spin_unlock(&x-lock);/ 回放定时器超时回调函数static void xfrm_replay_timer_handler(unsigned long data)struct xfrm_state *x = (struct xfrm_state*)data;spin_lock(&x-lock);/ 只是状态为有效时才检查if (x-km.state = XFRM_STATE_VALID) / 是否有NETLINK的监听者if (xfrm_aevent_is_on()/ 通知回放超时事件xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);else/ 设置通知推迟标志x-xflags |= XFRM_TIME_DEFER;spin_unlock(&x-lock);状态初始化:int xfrm_init_state(struct xfrm_state *x)struct xfrm_state_afinfo *afinfo;int family = x-props.family;int err;err = -EAFNOSUPPORT;/ 获取协议族信息结构afinfo = xfrm_state_get_afinfo(family);if (!afinfo)goto error;err = 0;/ 协议族信息初始化if (afinfo-init_flags)err = afinfo-init_flags(x);xfrm_state_put_afinfo(afinfo);if (err)goto error;err = -EPROTONOSUPPORT;/ 获取可用协议(ah, esp, ipcomp, ip)x-type = xfrm_get_type(to, family);if (x-type = NULL)goto error;err = x-type-init_state(x);if (err)goto error;/ 获取可用模式(transport, tunnel)x-mode = xfrm_get_mode(x-props.mode, family);if (x-mode = NULL)goto error;/ 状态设置为VALIDx-km.state = XFRM_STATE_VALID;error:return err;EXPORT_SYMBOL(xfrm_init_state);4.2 状态删除状态删除函数为xfrm_state_delete(), 该函数被pfkey_delete函数调用./ 这个函数只是_xfrm_state_delete()加锁的包裹函数int xfrm_state_delete(struct xfrm_state *x)int err;spin_lock_bh(&x-lock);err = _xfrm_state_delete(x);spin_unlock_bh(&x-lock);return err;EXPORT_SYMBOL(xfrm_state_delete);/ 实际的相同删除操作函数, 必须保证在x-lock加锁状态下执行int _xfrm_state_delete(struct xfrm_state *x)int err = -ESRCH;/ 如果状态已经是DEAD就不操作了if (x-km.state != XFRM_STATE_DEAD) / 设置状态为DEADx-km.state = XFRM_STATE_DEAD;/ xfrm_state_lock是全局的状态链表操作锁spin_lock(&xfrm_state_lock);/ 从目的地址索引的链表中断开hlist_del(&x-bydst);/ 从源地址索引的链表中断开hlist_del(&x-bysrc);/ 从SPI索引的链表中断开if (x-id.spi)hlist_del(&x-byspi);/ xfrm状态总数减一xfrm_state_num-;spin_unlock(&xfrm_state_lock);/* All xfrm_state objects are created by xfrm_state_alloc. * The xfrm_state_alloc call gives a reference, and that * is what we are dropping here. */ 减少该状态引用计数_xfrm_state_put(x);err = 0;return err;EXPORT_SYMBOL(_xfrm_state_delete);4.3 删除全部状态删除全部状态函数为xfrm_state_flush(), 该函数被pfkey_flush函数调用./ 删除某种协议proto的所有状态void xfrm_state_flush(u8 proto)int i;spin_lock_bh(&xfrm_state_lock);/ 循环所有HASH链表for (i = 0; i to, proto) / 先hold住状态,防止在解开xfrm_state_lock锁, 又没被进入xfrm_state_delete()前/ 被意外删除了, 此处考虑得比较仔细xfrm_state_hold(x);/ 先解开xfrm_state_lock, 在xfrm_state_delete()中要重新上锁spin_unlock_bh(&xfrm_state_lock);/ 删除状态xfrm_state_delete(x);/ 减少刚才的引用计数xfrm_state_put(x);/ 重新加锁, 循环spin_lock_bh(&xfrm_state_lock);goto restart;spin_unlock_bh(&xfrm_state_lock);wake_up(&km_waitq);EXPORT_SYMBOL(xfrm_state_flush);4.4 状态增加或更新状态增加函数为xfrm_state_add(), 状态更新函数为xfrm_state_update(),这两个函数都被pfkey_add函数调用./ 添加xfrm状态int xfrm_state_add(struct xfrm_state *x)struct xfrm_state *x1;int family;int err;/ 当协议为为ESP, AH, COMP以及ANY时为真, 其他为假int use_spi = xfrm_id_proto_match(to, IPSEC_PROTO_ANY);family = x-props.family;spin_lock_bh(&xfrm_state_lock);/ 根据xfrm的地址, SPI, 协议, 协议族等信息查找内核中是否已经存在相同的xfrmx1 = _xfrm_state_locate(x, use_spi, family);if (x1) / 确实已经存在, 返回错误xfrm_state_put(x1);x1 = NULL;err = -EEXIST;goto out;if (use_spi & x-km.seq) / 如果序列号有效, 根据序列号查找内核中是否已经存在相同的xfrmx1 = _xfrm_find_acq_byseq(x-km.seq);/ 找到, 但如果目的地址不符合的话, 仍试为没找到if (x1 & xfrm_addr_cmp(&x1-id.daddr, &x-id.daddr, family) xfrm_state_put(x1);x1 = NULL;/ 如果没找到x1, 根据各种信息再查找xfrmif (use_spi & !x1)x1 = _find_acq_core(family, x-props.mode, x-props.reqid, to, &x-id.daddr, &x-props.saddr, 0);/ 如果x和现在内核中的xfrm匹配的话为x生成genid参数/ 会用到一个静态单文件全局变量: xfrm_state_genid_xfrm_state_bump_genids(x);/ 将新xfrm插入内核的各xfrm表, 这些表是以HASH表形式实现的, 分别根据/ 源地址, 目的地址形成两个HASH表_xfrm_state_insert(x);err = 0;out:spin_unlock_bh(&xfrm_state_lock);/ 如果按后来的条件找到x1, 删除之, 该状态不需要了if (x1) / 将找到的x1从链表中删除,xfrm_state_delete(x1);/ 释放x1xfrm_state_put(x1);return err;EXPORT_SYMBOL(xfrm_state_add);/ 更新xfrm状态int xfrm_state_update(struct xfrm_state *x)struct xfrm_state *x1;int err;int use_spi = xfrm_id_proto_match(to, IPSEC_PROTO_ANY);spin_lock_bh(&xfrm_state_lock);/ 查找内核中相应的xfrm, 找不到的话出错x1 = _xfrm_state_locate(x, use_spi, x-props.family);err = -ESRCH;if (!x1)goto out;/ 如果该xfrm正在被IPSEC通道使用, 返回错误if (xfrm_state_kern(x1) xfrm_state_put(x1);err = -EEXIST;goto out;/ 找到的x1本来就是在acquire状态, 直接将x插入系统xfrm表就行了if (x1-km.state = XFRM_STATE_ACQ) _xfrm_state_insert(x);x = NULL;err = 0;out:spin_unlock_bh(&xfrm_state_lock);if (err)return err;if (!x) / 将找到的acquire状态的xfrm删除, 正确返回xfrm_state_delete(x1);xfrm_state_put(x1);return 0;/ 找到了x1, 状态也不是acquire, 即进行正常的更新x1中的数据为x的数据err = -EINVAL;spin_lock_bh(&x1-lock);if (likely(x1-km.state = XFRM_STATE_VALID) / 拷贝封装处理if (x-encap & x1-encap)memcpy(x1-encap, x-encap, sizeof(*x1-encap);/ 拷贝care of的地址if (x-coaddr & x1-coaddr) memcpy(x1-coaddr, x-coaddr, sizeof(*x1-coaddr);/ 没有SPI时拷贝选择子if (!use_spi & memcmp(&x1-sel, &x-sel, sizeof(x1-sel)memcpy(&x1-sel, &x-sel, sizeof(x1-sel);/ 拷贝生命期memcpy(&x1-lft, &x-lft, sizeof(x1-lft);x1-km.dying = 0;/ 1秒钟的超时mod_timer(&x1-timer, jiffies + HZ);if (x1-curlft.use_time)xfrm_state_check_expire(x1);err = 0;spin_unlock_bh(&x1-lock);xfrm_state_put(x1);return err;EXPORT_SYMBOL(xfrm_state_update);4.5 状态插入状态插入函数为xfrm_state_insert(), 该函数被ipcomp_tunnel_attach()函数(net/ipv4/ipcomp.c)调用/ xfrm_state_insert只是个包裹函数, 加xfrm_state_lock锁后调用_xfrm_state_bump_genids和/ _xfrm_state_insertvoid xfrm_state_insert(struct xfrm_state *x)spin_lock_bh(&xfrm_state_lock);_xfrm_state_bump_genids(x);_xfrm_state_insert(x);spin_unlock_bh(&xfrm_state_lock);EXPORT_SYMBOL(xfrm_state_insert);/* xfrm_state_lock is held */ 碰撞检查, 看是否有多个连接状态, 要进行区别static void _xfrm_state_bump_genids(struct xfrm_state *xnew)unsigned short family = xnew-props.family;u32 reqid = xnew-props.reqid;struct xfrm_state *x;struct hlist_node *entry;unsigned int h;/ 计算状态HASH值来找相关链表h = xfrm_dst_hash(&xnew-id.daddr, &xnew-props.saddr, reqid, family);hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) / 如果已经在链表中的状态的协议族, 请求ID, 源地址, 目的地址都和新状态匹配if (x-props.family= family & x-props.reqid= reqid & !xfrm_addr_cmp(&x-id.daddr, &xnew-id.daddr, family) & !xfrm_addr_cmp(&x-props.saddr, &xnew-props.saddr, family)/ 将这些状态的genid参数设置为当前xfrm_state_genid(全局变量)x-genid = xfrm_state_genid;static void _xfrm_state_insert(struct xfrm_state *x)unsigned int h;/ 将新状态的genid设置为当前xfrm_state_genid值加一,和其他碰撞的状态区分开x-genid = +xfrm_state_genid;/ 添加到按目的地址HASH的链表h = xfrm_dst_hash(&x-id.daddr, &x-props.saddr, x-props.reqid, x-props.family);hlist_add_head(&x-bydst, xfrm_state_bydst+h);/ 添加到按源地址HASH的链表h = xfrm_src_hash(&x-id.daddr, &x-props.saddr, x-props.family);hlist_add_head(&x-bysrc, xfrm_state_bysrc+h);if (x-id.spi) / 添加到按SPI进行HASH的链表h = xfrm_spi_hash(&x-id.daddr, x-id.spi, to, x-props.family);hlist_add_head(&x-byspi, xfrm_state_byspi+h);/ 修改定时器, 超时仅1秒mod_timer(&x-timer, jiffies + HZ);/ 如果设置了回放最大时间间隔, 超时改为该值if (x-replay_maxage)mod_timer(&x-rtimer, jiffies + x-replay_maxage);/ 唤醒等待队列wake_up(&km_waitq);/ 状态总数加1xfrm_state_num+;/ HASH扩大检查, 检查是否需要扩展HASH表数量xfrm_hash_grow_check(x-bydst.next != NULL);4.6 状态查找状态查找函数有好几个, 分别按不同条件来查找状态, 注意找到状态后, 都会增加状态的引用计数.4.6.1 xfrm_state_lookup/ 只是_xfrm_state_lookup的包裹函数, 是根据SPI进行HASH后查找struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, _be32 spi, u8 proto, unsigned short family)struct xfrm_state *x;spin_lock_bh(&xfrm_state_lock);x = _xfrm_state_lookup(daddr, spi, proto, family);spin_unlock_bh(&xfrm_state_lock);return x;EXPORT_SYMBOL(xfrm_state_lookup);static struct xfrm_state *_xfrm_state_lookup(xfrm_address_t *daddr, _be32 spi, u8 proto, unsigned short family)/ 根据SPI进行HASHunsigned int h = xfrm_spi_hash(daddr, spi, proto, family);struct xfrm_state *x;struct hlist_node *entry;/ 循环相应的SPI链表hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) / 比较协议族, SPI, 和协议是否相同if (x-props.family != family | x-id.spi != spi | to != proto)continue;/ 比较目的地址是否相同switch (family) case AF_INET:if (x-id.daddr.a4 != daddr-a4)continue;break;case AF_INET6:if (!ipv6_addr_equal(struct in6_addr *)daddr, (struct in6_addr *) x-id.daddr.a6)continue;break;/ 找到, 增加状态引用计数, 返回xfrm_state_hold(x);return x;return NULL;4.6.2 按地址查找状态/ 只是_xfrm_state_lookup_byaddr的包裹函数,是根据目的地址进行HASH后查找struct xfrm_state *xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)struct xfrm_state *x;spin_lock_bh(&xfrm_state_lock);x = _xfrm_state_lookup_byaddr(daddr, saddr, proto, family);spin_unlock_bh(&xfrm_state_lock);return x;EXPORT_SYMBOL(xfrm_state_lookup_byaddr);static struct xfrm_state *_xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family)/ 根据目的地址计算HASH值unsigned int h = xfrm_src_hash(daddr, saddr, family);struct xfrm_state *x;struct hlist_node *entry;/ 循环相应的源地址链表hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) / 比较协议族和协议是否相同if (x-props.family != family | to != proto)continue;/ 比较源地址和目的地址是否相同switch (family) case AF_INET:if (x-id.daddr.a4 != daddr-a4 | x-props.saddr.a4 != saddr-a4)continue;break;case AF_INET6:if (!ipv6_addr_equal(struct in6_addr *)daddr, (struct in6_addr *) x-id.daddr.a6) | !ipv6_addr_equal(struct in6_addr *)saddr, (struct in6_addr *) x-props.saddr.a6)continue;break;/ 找到, 增加状态引用计数, 返回xfrm_state_hold(x);return x;return NULL;4.6.3 _xfrm_state_locate这个函数只是_xfrm_state_lookup和_xfrm_state_lookup_byaddr的组合函数static inline struct xfrm_state *_xfrm_state_locate(struct xfrm_state *x, int use_spi, int family)if (use_spi)return _xfrm_state_lookup(&x-id.daddr, x-id.spi, to, family);elsereturn _xfrm_state_lookup_byaddr(&x-id.daddr, &x-props.saddr, to, family);4.6.4 查找ACQUIRE类型的状态ACQUIRE类型的SA的产生是内核发现数据需要进行保护, 但却没有找到相关的SA, 就向用户空间的IKE协商程序发送ACQUIRE请求, 并生成一个ACQUIRE类型的SA, 如果用户空间协议协商成功会生成合适的SA传内核, 内核就会替换此ACQUIRE的SA, 因此ACQUIRE不是真正可用的SA, 只是表示有此SA的需求, 等待用户空间程序协商结果。/ 只是_find_acq_core的包裹函数struct xfrm_state *xfrm_find_acq(u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create, unsigned short family)struct xfrm_state *x;spin_lock_bh(&xfrm_state_lock);x = _find_acq_core(family, mode, reqid, proto, daddr, saddr, create);spin_unlock_bh(&xfrm_state_lock);return x;EXPORT_SYMBOL(xfrm_find_acq);/* xfrm_state_lock is held */static struct xfrm_state *_find_acq_core(unsigned short family, u8 mode, u32 reqid, u8 proto, xfrm_address_t *daddr, xfrm_address_t *saddr, int create)/ 根据源地址,目的地址,请求ID进行目的地址类型HASHunsigned int h = xfrm_dst_hash(daddr, saddr, reqid, family);struct hlist_node *entry;struct xfrm_state *x;hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) / 比较请求ID,数据模式,协议族/ 要求状态的类型为XFRM_STATE_ACQ,SPI值为0if (x-props.reqid != reqid | x-props.mode != mode | x-props.family != family | x-km.state != XFRM_STATE_ACQ | x-id.spi != 0)continue;/ 再比较源地址和目的地址是否相同switch (family) case AF_INET:if (x-id.daddr.a4 != daddr-a4 | x-props.saddr.a4 != saddr-a4)continue;break;case AF_INET6:if (!ipv6_addr_equal(struct in6_addr *)x-id.daddr.a6, (struct in6_addr *)daddr) | !ipv6_addr_equal(struct in6_addr *) x-props.saddr.a6, (struct in6_addr *)saddr)continue;break;/ 找到, 增加状态引用计数, 返回xfrm_state_hold(x);return x;/ 没找到/ 如果不需要创建, 返回NULLif (!create)return NULL;/ 创建ACQ类型的xfrm_state/ 分配空间x = xfrm_state_alloc();if (likely(x) / 填写网络地址基本参数switch (family) case AF_INET:x-sel.daddr.a4 = daddr-a4;x-sel.saddr.a4 = saddr-a4;x-sel.prefixlen_d = 32;x-sel.prefixlen_s = 32;x-props.saddr.a4 = saddr-a4;x-id.daddr.a4 = daddr-a4;break;case AF_INET6:ipv6_addr_copy(struct in6_addr *)x-sel.daddr.a6, (struct in6_addr *)daddr);ipv6_addr_copy(struct in6_addr *)x-sel.saddr.a6, (struct in6_addr *)saddr);x-sel.prefixlen_d = 128;x-sel.prefixlen_s = 128;ipv6_addr_copy(struct in6_addr *)x-props.saddr.a6, (struct in6_addr *)saddr);ipv6_addr_copy(struct in6_addr *)x-id.daddr.a6, (struct in6_addr *)daddr);break;/ 状态类型设置为XFRM_STATE_ACQx-km.state = XFRM_STATE_ACQ;/ 状态其他参数赋值to = proto;x-props.family = family;x-props.mode = mode;x-props.reqid = reqid;/ 硬性可增加的超时x-lft.hard_add_expires_seconds = XFRM_ACQ_
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年肾脏科肾病综合治疗方案测评答案及解析
- 地下供热管线改造交通疏导方案
- 防水工程材料进场检验管理方案
- 2025年运动医学康复治疗方案设计考核卷答案及解析
- 林木种质资源库运维管理方案
- 广东省肇庆市2024-2025年高中地理 限时训练六评讲说课稿 新人教版必修2
- 2025年心理咨询与治疗技术测试卷答案及解析
- 2025(参考)设备采购合同(标准中英)样式例文办公文档
- 2025年秋季新九年级开学摸底考历史模拟卷及答案
- 2025年消化内科疾病诊断与治疗模拟练习答案及解析
- 医疗科室外包合同协议书
- 基于核心素养的中小学安全教育课程设计与实施路径
- 超级充电综合站及配套设施建设项目可行性研究报告
- 2025年湖北省武汉市中考语文真题(含答案)
- 2025-2026学年人教大同版(2024)小学英语三年级上册教学计划及进度表
- Unit1Weletotheunit课件译林版八年级英语上册
- 离职交接事项协议书范本
- 2025-2026学年陕旅版(三起)(2024)小学英语四年级上册(全册)教学设计(附目录)
- 心电监护血氧饱和度监测技术
- 【高考真题】海南省2025年高考真题物理(含答案)
- 体育教师自我介绍课件
评论
0/150
提交评论