`
shaojiashuai123456
  • 浏览: 266408 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
社区版块
存档分类
最新评论

Linux内核中的IPSEC实现(6) --转载

阅读更多

本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@hotmail.com
来源:http://yfydz.cublog.cn


8. 安全协议

与IPSEC相关的安全协议是AH(51)和ESP(50), IPSEC使用这两个协议对普通数据包进行封装, AH只认证不加密, ESP既加密又认证, 当ESP和AH同时使用时, 一般都是先进行ESP封装, 再进行AH封装, 因为AH是对整个IP包进行验证的, 而ESP只验证负载部分.

在IPV4下的AH和ESP的协议实现在net/ipv4/ah4.c和net/ipv4/esp4.c中, 每个协议实现实际是要完成两个结构: struct net_protocol和struct xfrm_type, 前者用于处理接收的该协议类型的IP包, 后者则是IPSEC协议处理.

8.1 AH

8.1.1 初始化

/* net/ipv4/ah4.c */
static int __init ah4_init(void)
{
// 登记AH协议的xfrm协议处理结构
 if (xfrm_register_type(&ah_type, AF_INET) < 0) {
  printk(KERN_INFO "ip ah init: can't add xfrm type\n");
  return -EAGAIN;
 }
// 登记AH协议到IP协议
 if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) < 0) {
  printk(KERN_INFO "ip ah init: can't add protocol\n");
  xfrm_unregister_type(&ah_type, AF_INET);
  return -EAGAIN;
 }
 return 0;
}

8.1.2 IPV4下的AH协议处理结构

// AH协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol字段选择相应的上层协议处理
// 函数, 当IP协议号是51时, 数据包将调用该结构的handler处理函数:
static struct net_protocol ah4_protocol = {
 .handler = xfrm4_rcv,
 .err_handler = ah4_err,
 .no_policy = 1,
};
AH协议结构的handler函数为xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义, 在上一篇中进行了介绍.

// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包
static void ah4_err(struct sk_buff *skb, u32 info)
{
// 应用层, data指向ICMP错误包里的内部IP头
 struct iphdr *iph = (struct iphdr*)skb->data;
// AH头
 struct ip_auth_hdr *ah = (struct ip_auth_hdr*)(skb->data+(iph->ihl<<2));
 struct xfrm_state *x;
// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误
 if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
     skb->h.icmph->code != ICMP_FRAG_NEEDED)
  return;
// 重新查找SA
 x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET);
 if (!x)
  return;
 printk(KERN_DEBUG "pmtu discovery on SA AH/%08x/%08x\n",
        ntohl(ah->spi), ntohl(iph->daddr));
 xfrm_state_put(x);
}

8.1.3 AH4协议的IPSEC处理结构

// AH4的xfrm协议处理结构
static struct xfrm_type ah_type =
{
 .description = "AH4",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_AH,
// 状态初始化
 .init_state = ah_init_state,
// 协议释放
 .destructor = ah_destroy,
// 协议输入
 .input  = ah_input,
// 协议输出
 .output  = ah_output
};
结构的重点是input和ouput函数

8.1.3.1 状态初始化
ah_data数据结构:
/* include/net/ah.h */
struct ah_data
{
// 密钥指针
 u8   *key;
// 密钥长度
 int   key_len;
// 工作初始化向量
 u8   *work_icv;
// 初始化向量完整长度
 int   icv_full_len;
// 初始化向量截断长度
 int   icv_trunc_len;
// HASH算法
 struct crypto_hash *tfm;
};

// 该函数被xfrm状态(SA)初始化函数xfrm_init_state调用
// 用来生成SA中所用的AH数据处理结构相关信息
static int ah_init_state(struct xfrm_state *x)
{
 struct ah_data *ahp = NULL;
 struct xfrm_algo_desc *aalg_desc;
 struct crypto_hash *tfm;
// 对AH协议的SA, 认证算法是必须的, 否则就没法进行AH认证了
 if (!x->aalg)
  goto error;
 /* null auth can use a zero length key */
// 认证算法密钥长度要大于512
 if (x->aalg->alg_key_len > 512)
  goto error;
// 如果要进行UDP封装(进行NAT穿越), 错误, 因为AH是不支持NAT的
 if (x->encap)
  goto error;
// 分配ah_data数据结构空间
 ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);
 if (ahp == NULL)
  return -ENOMEM;
// 设置AH数据结构的密钥和长度
 ahp->key = x->aalg->alg_key;
 ahp->key_len = (x->aalg->alg_key_len+7)/8;
// 分配认证算法HASH结构指针并赋值给AH数据结构
// 算法是固定相同的, 但在每个应用使用算法时的上下文是不同的, 该结构就是描述具体应用
// 时的相关处理的上下文数据的
 tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);
 if (IS_ERR(tfm))
  goto error;
 ahp->tfm = tfm;
// 设置认证算法密钥
 if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len))
  goto error;
 
 /*
  * Lookup the algorithm description maintained by xfrm_algo,
  * verify crypto transform properties, and store information
  * we need for AH processing.  This lookup cannot fail here
  * after a successful crypto_alloc_hash().
  */
// 分配算法描述结构
 aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
 BUG_ON(!aalg_desc);
 if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
     crypto_hash_digestsize(tfm)) {
  printk(KERN_INFO "AH: %s digestsize %u != %hu\n",
         x->aalg->alg_name, crypto_hash_digestsize(tfm),
         aalg_desc->uinfo.auth.icv_fullbits/8);
  goto error;
 }
// AH数据结构的初始化向量的总长和截断长度的赋值 
 ahp->icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
 ahp->icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
 
 BUG_ON(ahp->icv_trunc_len > MAX_AH_AUTH_LEN);
// 分配初始化向量空间, 没对其赋值, 其初始值就是随机值, 这也是初始化向量所需要的
 ahp->work_icv = kmalloc(ahp->icv_full_len, GFP_KERNEL);
 if (!ahp->work_icv)
  goto error;
// AH类型SA中AH头长度: ip_auth_hdr结构和初始化向量长度, 按8字节对齐 
// 反映在AH封装操作时要将数据包增加的长度
 x->props.header_len = XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len);
// 如果是通道模式, 增加IP头长度
 if (x->props.mode == XFRM_MODE_TUNNEL)
  x->props.header_len += sizeof(struct iphdr);
// SA数据指向AH数据结构
 x->data = ahp;
 return 0;
error:
 if (ahp) {
  kfree(ahp->work_icv);
  crypto_free_hash(ahp->tfm);
  kfree(ahp);
 }
 return -EINVAL;
}

8.1.3.2 协议释放
// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用
static void ah_destroy(struct xfrm_state *x)
{
 struct ah_data *ahp = x->data;
 if (!ahp)
  return;
// 释放初始化向量空间
 kfree(ahp->work_icv);
 ahp->work_icv = NULL;
// 算法描述释放
 crypto_free_hash(ahp->tfm);
 ahp->tfm = NULL;
// AH数据结构释放
 kfree(ahp);
}

8.1.3.3 协议输入

// 接收数据处理, 在xfrm4_rcv_encap()函数中调用
// 进行AH认证, 剥离AH头
static int ah_input(struct xfrm_state *x, struct sk_buff *skb)
{
 int ah_hlen;
 int ihl;
 int err = -EINVAL;
 struct iphdr *iph;
 struct ip_auth_hdr *ah;
 struct ah_data *ahp;
// IP头备份空间
 char work_buf[60];
// skb数据包要准备留出AH头空间
 if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))
  goto out;
// IP上层数据为AH数据
 ah = (struct ip_auth_hdr*)skb->data;
// SA相关的AH处理数据
 ahp = x->data;
 ah_hlen = (ah->hdrlen + 2) << 2;
// AH头部长度合法性检查
 if (ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_full_len) &&
     ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len))
  goto out;
// skb数据包要准备留出实际AH头空间
 if (!pskb_may_pull(skb, ah_hlen))
  goto out;
 /* We are going to _remove_ AH header to keep sockets happy,
  * so... Later this can change. */
// 对于clone的包要复制成独立包
 if (skb_cloned(skb) &&
     pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
  goto out;
 skb->ip_summed = CHECKSUM_NONE;
// 可能包已经进行了复制, 所以对ah重新赋值
 ah = (struct ip_auth_hdr*)skb->data;
 iph = skb->nh.iph;
// IP头长度
 ihl = skb->data - skb->nh.raw;
// 备份外部IP头数据
 memcpy(work_buf, iph, ihl);
// 将IP头中的一些参数清零, 这些参数不进行认证
 iph->ttl = 0;
 iph->tos = 0;
 iph->frag_off = 0;
 iph->check = 0;
// IP头长度超过20字节时,处理IP选项参数
 if (ihl > sizeof(*iph)) {
  u32 dummy;
  if (ip_clear_mutable_options(iph, &dummy))
   goto out;
 }
        {
// 认证数据缓冲区
  u8 auth_data[MAX_AH_AUTH_LEN];
// 拷贝数据包中的认证数据到缓冲区
  memcpy(auth_data, ah->auth_data, ahp->icv_trunc_len);
// 包括IP头部分数据
  skb_push(skb, ihl);
// 计算认证值是否匹配, 非0表示出错
  err = ah_mac_digest(ahp, skb, ah->auth_data);
// 认证失败返回错误
  if (err)
   goto out;
  err = -EINVAL;
// 复制一定长度的认证数据作为初始化向量
  if (memcmp(ahp->work_icv, auth_data, ahp->icv_trunc_len)) {
   x->stats.integrity_failed++;
   goto out;
  }
 }
// 将备份的IP头缓冲区中的协议改为AH内部包裹的协议
 ((struct iphdr*)work_buf)->protocol = ah->nexthdr;
// 将原来IP头数据拷贝到原来AH头后面作为新IP头
 skb->h.raw = memcpy(skb->nh.raw += ah_hlen, work_buf, ihl);
// skb包缩减原来的IP头和AH头, 以新IP头作为数据开始
 __skb_pull(skb, ah_hlen + ihl);
 return 0;
out:
 return err;
}

8.1.3.4 协议输出

// 发送数据处理, 在xfrm4_output_one()中调用
// 计算AH认证值, 添加AH头
static int ah_output(struct xfrm_state *x, struct sk_buff *skb)
{
 int err;
 struct iphdr *iph, *top_iph;
 struct ip_auth_hdr *ah;
 struct ah_data *ahp;
// 临时IP头缓冲区, 最大IP头60字节
 union {
  struct iphdr iph;
  char   buf[60];
 } tmp_iph;
// 当前的IP头将作为最外部IP头
 top_iph = skb->nh.iph;
// 临时IP头,用于临时保存IP头内部分字段数据
 iph = &tmp_iph.iph;
// 将当前IP头中不进行认证的字段数据复制到临时IP头
 iph->tos = top_iph->tos;
 iph->ttl = top_iph->ttl;
 iph->frag_off = top_iph->frag_off;
// 如果有IP选项, 处理IP选项
 if (top_iph->ihl != 5) {
  iph->daddr = top_iph->daddr;
  memcpy(iph+1, top_iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
  err = ip_clear_mutable_options(top_iph, &top_iph->daddr);
  if (err)
   goto error;
 }
// AH头定位在外部IP头后面, skb缓冲中已经预留出AH头的数据部分了,
// 这是通过mode->output函数预留的, 通常调用type->output前要调用mode->oputput
 ah = (struct ip_auth_hdr *)((char *)top_iph+top_iph->ihl*4);
// AH中的下一个头用原来的外部IP头中的协议
 ah->nexthdr = top_iph->protocol;
// 将外部IP头的不进行认证计算的部分字段清零
 top_iph->tos = 0;
 top_iph->tot_len = htons(skb->len);
 top_iph->frag_off = 0;
 top_iph->ttl = 0;
// IP协议改为AH
 top_iph->protocol = IPPROTO_AH;
 top_iph->check = 0;
// AH数据处理结构
 ahp = x->data;
// AH头长度对齐
 ah->hdrlen  = (XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +
       ahp->icv_trunc_len) >> 2) - 2;
// AH头参数赋值
 ah->reserved = 0;
// SPI值
 ah->spi = x->id.spi;
// 序列号
 ah->seq_no = htonl(++x->replay.oseq);
// 通知防止重放攻击处理, 更新序列号
 xfrm_aevent_doreplay(x);
// 对skb进行AH认证值的计算
 err = ah_mac_digest(ahp, skb, ah->auth_data);
 if (err)
  goto error;
// 赋值初始化向量值到认证数据部分
 memcpy(ah->auth_data, ahp->work_icv, ahp->icv_trunc_len);
// 恢复原来IP头的的不认证部分的值
 top_iph->tos = iph->tos;
 top_iph->ttl = iph->ttl;
 top_iph->frag_off = iph->frag_off;
 if (top_iph->ihl != 5) {
  top_iph->daddr = iph->daddr;
  memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));
 }
// 重新计算IP头的认证值
 ip_send_check(top_iph);
 err = 0;
error:
 return err;
}
 
8.2 ESP

8.2.1 初始化

/* net/ipv4/esp4.c */
static int __init esp4_init(void)
{
// 登记ESP协议的xfrm协议处理结构
 if (xfrm_register_type(&esp_type, AF_INET) < 0) {
  printk(KERN_INFO "ip esp init: can't add xfrm type\n");
  return -EAGAIN;
 }
// 登记ESP协议到IP协议
 if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {
  printk(KERN_INFO "ip esp init: can't add protocol\n");
  xfrm_unregister_type(&esp_type, AF_INET);
  return -EAGAIN;
 }
 return 0;
}

8.2.2 IPV4下的ESP协议处理结构

// ESP协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol
// 字段选择相应的上层协议处理函数, 当IP协议号是50时, 数据包将
// 调用该结构的handler处理函数:
static struct net_protocol esp4_protocol = {
 .handler = xfrm4_rcv,
 .err_handler = esp4_err,
 .no_policy = 1,
};

ESP协议结构的handler函数也是xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义,
在上一篇中进行了介绍.

// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包
static void esp4_err(struct sk_buff *skb, u32 info)
{
// 应用层, data指向ICMP错误包里的内部IP头
 struct iphdr *iph = (struct iphdr*)skb->data;
// ESP头
 struct ip_esp_hdr *esph = (struct ip_esp_hdr*)(skb->data+(iph->ihl<<2));
 struct xfrm_state *x;
// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误
 if (skb->h.icmph->type != ICMP_DEST_UNREACH ||
     skb->h.icmph->code != ICMP_FRAG_NEEDED)
  return;
// 重新查找SA
 x = xfrm_state_lookup((xfrm_address_t *)&iph->daddr, esph->spi, IPPROTO_ESP, AF_INET);
 if (!x)
  return;
 NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",
   ntohl(esph->spi), ntohl(iph->daddr));
 xfrm_state_put(x);
}

8.2.3 ESP4协议的IPSEC处理结构

static struct xfrm_type esp_type =
{
 .description = "ESP4",
 .owner  = THIS_MODULE,
 .proto       = IPPROTO_ESP,
// 状态初始化
 .init_state = esp_init_state,
// 协议释放
 .destructor = esp_destroy,
// 计算最大长度
 .get_max_size = esp4_get_max_size,
// 协议输入
 .input  = esp_input,
// 协议输出
 .output  = esp_output
};

8.2.3.1 状态初始化
esp_data数据结构:
/* include/net/esp.h */
struct esp_data
{
 struct scatterlist  sgbuf[ESP_NUM_FAST_SG];
 /* Confidentiality */
// 加密使用的相关数据
 struct {
// 密钥
  u8   *key;  /* Key */
// 密钥长度
  int   key_len; /* Key length */
// 填充长度
  int   padlen;  /* 0..255 */
  /* ivlen is offset from enc_data, where encrypted data start.
   * It is logically different of crypto_tfm_alg_ivsize(tfm).
   * We assume that it is either zero (no ivec), or
   * >= crypto_tfm_alg_ivsize(tfm). */
// 初始化向量长度
  int   ivlen;
// 初始化向量是否初始化标志
  int   ivinitted;
// 初始化向量
  u8   *ivec;  /* ivec buffer */
// 加密算法
  struct crypto_blkcipher *tfm;  /* crypto handle */
 } conf;
 /* Integrity. It is active when icv_full_len != 0 */
// 认证使用的相关数据
 struct {
// 密钥
  u8   *key;  /* Key */
// 密钥长度
  int   key_len; /* Length of the key */
// 初始化向量
  u8   *work_icv;
// 初始化向量全长
  int   icv_full_len;
// 初始化向量截断长度
  int   icv_trunc_len;
// 初始化向量更新函数, 好象没用
  void   (*icv)(struct esp_data*,
                                 struct sk_buff *skb,
                                 int offset, int len, u8 *icv);
// HASH算法
  struct crypto_hash *tfm;
 } auth;
};
// ESP的esp_data数据结构初始化
static int esp_init_state(struct xfrm_state *x)
{
 struct esp_data *esp = NULL;
 struct crypto_blkcipher *tfm;
 /* null auth and encryption can have zero length keys */
// 如果有认证算法, 密钥至少512, ESP的认证处理是可选的, 但在实际中都会使用认证
 if (x->aalg) {
  if (x->aalg->alg_key_len > 512)
   goto error;
 }
// ESP加密算法是必须的
 if (x->ealg == NULL)
  goto error;
// 分配esp_data数据结构空间
 esp = kzalloc(sizeof(*esp), GFP_KERNEL);
 if (esp == NULL)
  return -ENOMEM;
// 如果定义了认证算法, 初始化认证算法参数, 和AH类似
 if (x->aalg) {
  struct xfrm_algo_desc *aalg_desc;
  struct crypto_hash *hash;
// 认证密钥和长度设置
  esp->auth.key = x->aalg->alg_key;
  esp->auth.key_len = (x->aalg->alg_key_len+7)/8;
// 分配HASH算法的实现
  hash = crypto_alloc_hash(x->aalg->alg_name, 0,
      CRYPTO_ALG_ASYNC);
  if (IS_ERR(hash))
   goto error;
  esp->auth.tfm = hash;
// 设置HASH算法密钥
  if (crypto_hash_setkey(hash, esp->auth.key, esp->auth.key_len))
   goto error;
// 找到算法描述
  aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);
  BUG_ON(!aalg_desc);
// 检查算法初始化向量长度合法性
  if (aalg_desc->uinfo.auth.icv_fullbits/8 !=
      crypto_hash_digestsize(hash)) {
   NETDEBUG(KERN_INFO "ESP: %s digestsize %u != %hu\n",
     x->aalg->alg_name,
     crypto_hash_digestsize(hash),
     aalg_desc->uinfo.auth.icv_fullbits/8);
   goto error;
  }
// 初始化向量的全长和截断长度
  esp->auth.icv_full_len = aalg_desc->uinfo.auth.icv_fullbits/8;
  esp->auth.icv_trunc_len = aalg_desc->uinfo.auth.icv_truncbits/8;
// 分配全长度的初始化向量空间
  esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);
  if (!esp->auth.work_icv)
   goto error;
 }
// 初始化加密算法相关参数, ESP使用的加密算法都是对称块加密算法, 不可能用非对称算法的
// 加密密钥
 esp->conf.key = x->ealg->alg_key;
// 加密密钥长度
 esp->conf.key_len = (x->ealg->alg_key_len+7)/8;
// 分配加密算法的具体实现结构
 tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);
 if (IS_ERR(tfm))
  goto error;
 esp->conf.tfm = tfm;
// 初始化向量大小
 esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);
// 填充数据长度初始化为0
 esp->conf.padlen = 0;
// 初始化向量长度非0, 分配具体的初始化向量空间
 if (esp->conf.ivlen) {
  esp->conf.ivec = kmalloc(esp->conf.ivlen, GFP_KERNEL);
  if (unlikely(esp->conf.ivec == NULL))
   goto error;
  esp->conf.ivinitted = 0;
 }
// 设置加密算法密钥
 if (crypto_blkcipher_setkey(tfm, esp->conf.key, esp->conf.key_len))
  goto error;
// 定义SA中ESP头部长度: ESP头加初始化向量长度
// 反映在ESP封装操作时要将数据包增加的长度
 x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;
// 如果是通道模式, 还需要增加IP头长度
 if (x->props.mode == XFRM_MODE_TUNNEL)
  x->props.header_len += sizeof(struct iphdr);
// 如果要进行UDP封装
 if (x->encap) {
  struct xfrm_encap_tmpl *encap = x->encap;
  switch (encap->encap_type) {
  default:
   goto error;
  case UDP_ENCAP_ESPINUDP:
// 该类型封装增加UDP头长度
   x->props.header_len += sizeof(struct udphdr);
   break;
  case UDP_ENCAP_ESPINUDP_NON_IKE:
// 该类型封装增加UDP头长度外加加8字节
   x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);
   break;
  }
 }
// 将esp_data作为SA的data指针
 x->data = esp;
// 追踪长度, 最大增加长度和当前的计算的增加长度的差值,在路由时会用到
// 对于AH, 由于没有定义get_max_size(), 该值位0
 x->props.trailer_len = esp4_get_max_size(x, 0) - x->props.header_len;
 return 0;
error:
 x->data = esp;
 esp_destroy(x);
 x->data = NULL;
 return -EINVAL;
}

8.2.3.2 协议释放

// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用
static void esp_destroy(struct xfrm_state *x)
{
 struct esp_data *esp = x->data;
 if (!esp)
  return;
// 释放加密算法
 crypto_free_blkcipher(esp->conf.tfm);
 esp->conf.tfm = NULL;
// 释放加密初始化向量
 kfree(esp->conf.ivec);
 esp->conf.ivec = NULL;
// 释放认证算法
 crypto_free_hash(esp->auth.tfm);
 esp->auth.tfm = NULL;
// 释放认证初始化向量
 kfree(esp->auth.work_icv);
 esp->auth.work_icv = NULL;
// 释放esp_data
 kfree(esp);
}

8.2.3.3 计算最大长度

// 在xfrm_state_mtu()函数中调用, 计算最大增加的数据长度
// AH中没有该函数, 增加的长度使用x->props.header_len
static u32 esp4_get_max_size(struct xfrm_state *x, int mtu)
{
 struct esp_data *esp = x->data;
// 加密块长度, 按4字节对齐
 u32 blksize = ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);
 int enclen = 0;
 switch (x->props.mode) {
 case XFRM_MODE_TUNNEL:
// 通道模式下的MTU, 按加密块大小对齐, +2是要包括2字节数据长度
  mtu = ALIGN(mtu +2, blksize);
  break;
 default:
 case XFRM_MODE_TRANSPORT:
  /* The worst case */
// 传输模式下, MTU先按4字节对齐, 再加块长度减4
  mtu = ALIGN(mtu + 2, 4) + blksize - 4;
  break;
 case XFRM_MODE_BEET:
   /* The worst case. */
  enclen = IPV4_BEET_PHMAXLEN;
  mtu = ALIGN(mtu + enclen + 2, blksize);
  break;
 }
// 如果加密算法中定义了填充长度, MTU也要按填充长度对齐
 if (esp->conf.padlen)
  mtu = ALIGN(mtu, esp->conf.padlen);
// 返回MTU加提议中需要增加的头部长度和认证初始化向量的截断长度
// enclen只在BEET模式下非0, 在通道和传输模式下都是0
 return mtu + x->props.header_len + esp->auth.icv_trunc_len - enclen;
}
 
8.2.3.4 协议输入

struct scatterlist结构说明:
/* include/asm-i386/scatterlist.h */
struct scatterlist {
    struct page  *page;
    unsigned int offset;
    dma_addr_t  dma_address;
    unsigned int length;
};

/*
 * Note: detecting truncated vs. non-truncated authentication data is very
 * expensive, so we only support truncated data, which is the recommended
 * and common case.
 */
// 接收数据处理, 在xfrm4_rcv_encap()函数中调用
// 进行ESP认证解密, 剥离ESP头, 解密成普通数据包, 数据包长度减少
// 输入的数据包是ESP包
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
 struct iphdr *iph;
 struct ip_esp_hdr *esph;
 struct esp_data *esp = x->data;
 struct crypto_blkcipher *tfm = esp->conf.tfm;
 struct blkcipher_desc desc = { .tfm = tfm };
 struct sk_buff *trailer;
 int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
// 认证初始化向量截断长度
 int alen = esp->auth.icv_trunc_len;
// 需要加密的数据长度: 总长减ESP头, 加密初始化向量长度, 认证初始化向量长度
 int elen = skb->len - sizeof(struct ip_esp_hdr) - esp->conf.ivlen - alen;
 int nfrags;
 int ihl;
 u8 nexthdr[2];
 struct scatterlist *sg;
 int padlen;
 int err;
// 在skb头留出ESP头的空间
 if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))
  goto out;
// 检查需要加密的数据长度, 必须大于0而且按块大小对齐的
 if (elen <= 0 || (elen & (blksize-1)))
  goto out;
 /* If integrity check is required, do this. */
// 认证计算处理
 if (esp->auth.icv_full_len) {
  u8 sum[alen];
// 计算认证值, 认证值保存在esp_data结构中
  err = esp_mac_digest(esp, skb, 0, skb->len - alen);
  if (err)
   goto out;
// 将skb中的认证初始化向量部分数据拷贝到缓冲区sum中
  if (skb_copy_bits(skb, skb->len - alen, sum, alen))
   BUG();
// 比较sum中的向量值和认证算法结构中的向量值是否匹配, 数据包正常情况下应该是相同的
  if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) {
   x->stats.integrity_failed++;
   goto out;
  }
 }
// 使数据包是可写的
 if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0)
  goto out;
 skb->ip_summed = CHECKSUM_NONE;
// 定位在数据包中的ESP头位置, 为当前的data位置
 esph = (struct ip_esp_hdr*)skb->data;
 /* Get ivec. This can be wrong, check against another impls. */
// 设置加密算法的初始化向量
 if (esp->conf.ivlen)
  crypto_blkcipher_set_iv(tfm, esph->enc_data, esp->conf.ivlen);
 sg = &esp->sgbuf[0];
 if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
  sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
  if (!sg)
   goto out;
 }
 skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) + esp->conf.ivlen, elen);
// 解密操作, 返回非0表示失败
 err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);
 if (unlikely(sg != &esp->sgbuf[0]))
  kfree(sg);
// 解密失败返回
 if (unlikely(err))
  return err;
// 拷贝两字节数据
 if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))
  BUG();
 padlen = nexthdr[0];
 if (padlen+2 >= elen)
  goto out;
 /* ... check padding bits here. Silly. :-) */
// 新的IP头
 iph = skb->nh.iph;
 ihl = iph->ihl * 4;
// 如果是NAT穿越情况, 进行一些处理
 if (x->encap) {
// xfrm封装模板
  struct xfrm_encap_tmpl *encap = x->encap;
// 定位UDP数据头位置, 在IP头之后
  struct udphdr *uh = (void *)(skb->nh.raw + ihl);
  /*
   * 1) if the NAT-T peer's IP or port changed then
   *    advertize the change to the keying daemon.
   *    This is an inbound SA, so just compare
   *    SRC ports.
   */
// 如果IP头源地址和SA提议中的源地址不同或源端口不同
  if (iph->saddr != x->props.saddr.a4 ||
      uh->source != encap->encap_sport) {
   xfrm_address_t ipaddr;
// 保存当前IP头源地址
   ipaddr.a4 = iph->saddr;
// 进行NAT通知回调处理
   km_new_mapping(x, &ipaddr, uh->source);
    
   /* XXX: perhaps add an extra
    * policy check here, to see
    * if we should allow or
    * reject a packet from a
    * different source
    * address/port.
    */
  }
 
  /*
   * 2) ignore UDP/TCP checksums in case
   *    of NAT-T in Transport Mode, or
   *    perform other post-processing fixes
   *    as per draft-ietf-ipsec-udp-encaps-06,
   *    section 3.1.2
   */
// 如果是传输模式或BEET模式, 设置不需要计算校验和
  if (x->props.mode == XFRM_MODE_TRANSPORT ||
      x->props.mode == XFRM_MODE_BEET)
   skb->ip_summed = CHECKSUM_UNNECESSARY;
 }
// 新IP头中协议
 iph->protocol = nexthdr[1];
// 缩减skb数据包长度
 pskb_trim(skb, skb->len - alen - padlen - 2);
// 重新定位IP上层数据头位置
 skb->h.raw = __skb_pull(skb, sizeof(*esph) + esp->conf.ivlen) - ihl;
 return 0;
out:
 return -EINVAL;
}

8.2.3.4 协议输出

// 发送数据处理, 在xfrm4_output_one()中调用
// 添加ESP头, 对数据包进行加密和认证处理, 数据包长度扩大
// 在NAT穿越情况下会封装为UDP数据
static int esp_output(struct xfrm_state *x, struct sk_buff *skb)
{
 int err;
 struct iphdr *top_iph;
 struct ip_esp_hdr *esph;
 struct crypto_blkcipher *tfm;
 struct blkcipher_desc desc;
 struct esp_data *esp;
 struct sk_buff *trailer;
 int blksize;
 int clen;
 int alen;
 int nfrags;
 /* Strip IP+ESP header. */
// 缩减skb数据, 减去IP头和ESP头, 剩下的数据就是要进行加密和认证的部分
 __skb_pull(skb, skb->h.raw - skb->data);
 /* Now skb is pure payload to encrypt */
 err = -ENOMEM;
 /* Round to block size */
// 加密块的初始值
 clen = skb->len;
// 获取SA的esp_data数据结构
 esp = x->data;
// 认证初始化向量截断长度
 alen = esp->auth.icv_trunc_len;
// 加密算法
 tfm = esp->conf.tfm;
// 给块加密算法描述结构赋值
 desc.tfm = tfm;
 desc.flags = 0;
// 每个加密块大小
 blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);
// 对齐要加密的数据总长
 clen = ALIGN(clen + 2, blksize);
// 如果要考虑填充, 继续对齐
 if (esp->conf.padlen)
  clen = ALIGN(clen, esp->conf.padlen);
// 使数据包可写
 if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)
  goto error;
 /* Fill padding... */
// 长度对齐后填充多余长度部分内容
 do {
  int i;
  for (i=0; i<clen-skb->len - 2; i++)
   *(u8*)(trailer->tail + i) = i+1;
 } while (0);
// 最后两字节表示填充数据的长度
 *(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;
 pskb_put(skb, trailer, clen - skb->len);
// 在将IP头部分扩展回来
 __skb_push(skb, skb->data - skb->nh.raw);
// 现在的IP头作为外部IP头
 top_iph = skb->nh.iph;
// esp头跟在IP头后
 esph = (struct ip_esp_hdr *)(skb->nh.raw + top_iph->ihl*4);
// 数据总长增加认证部分长度
 top_iph->tot_len = htons(skb->len + alen);
 *(u8*)(trailer->tail - 1) = top_iph->protocol;
 /* this is non-NULL only with UDP Encapsulation */
 if (x->encap) {
// NAT穿越情况下要将数据封装为UDP包
  struct xfrm_encap_tmpl *encap = x->encap;
  struct udphdr *uh;
  u32 *udpdata32;
// IP头后改为UDP头
  uh = (struct udphdr *)esph;
// 填充UDP头参数, 源端口, 目的端口, UDP数据长度
  uh->source = encap->encap_sport;
  uh->dest = encap->encap_dport;
  uh->len = htons(skb->len + alen - top_iph->ihl*4);
// 校验和为0, 表示不需要计算校验和, ESP本身就进行认证了
  uh->check = 0;
  switch (encap->encap_type) {
  default:
  case UDP_ENCAP_ESPINUDP:
// 在该模式下ESP头跟在UDP头后面
   esph = (struct ip_esp_hdr *)(uh + 1);
   break;
  case UDP_ENCAP_ESPINUDP_NON_IKE:
// 在该模式下ESP头跟在UDP头后面8字节处
   udpdata32 = (u32 *)(uh + 1);
   udpdata32[0] = udpdata32[1] = 0;
   esph = (struct ip_esp_hdr *)(udpdata32 + 2);
   break;
  }
// 外部IP头协议是UDP
  top_iph->protocol = IPPROTO_UDP;
 } else
// 非NAT穿越情况下, 外部IP头中的协议是ESP
  top_iph->protocol = IPPROTO_ESP;
// 填充ESP头中的SPI和序列号
 esph->spi = x->id.spi;
 esph->seq_no = htonl(++x->replay.oseq);
// 序列号更新通知回调
 xfrm_aevent_doreplay(x);
// 如果加密初始化向量长度非零, 设置加密算法中的初始化向量
 if (esp->conf.ivlen) {
  if (unlikely(!esp->conf.ivinitted)) {
   get_random_bytes(esp->conf.ivec, esp->conf.ivlen);
   esp->conf.ivinitted = 1;
  }
  crypto_blkcipher_set_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
 }
// 加密操作
 do {
  struct scatterlist *sg = &esp->sgbuf[0];
  if (unlikely(nfrags > ESP_NUM_FAST_SG)) {
   sg = kmalloc(sizeof(struct scatterlist)*nfrags, GFP_ATOMIC);
   if (!sg)
    goto error;
  }
  skb_to_sgvec(skb, sg, esph->enc_data+esp->conf.ivlen-skb->data, clen);
// 对数据加密
  err = crypto_blkcipher_encrypt(&desc, sg, sg, clen);
  if (unlikely(sg != &esp->sgbuf[0]))
   kfree(sg);
 } while (0);
 if (unlikely(err))
  goto error;
// 将加密算法初始化向量拷贝到数据包
 if (esp->conf.ivlen) {
  memcpy(esph->enc_data, esp->conf.ivec, esp->conf.ivlen);
  crypto_blkcipher_get_iv(tfm, esp->conf.ivec, esp->conf.ivlen);
 }
// 认证计算, 计算出HASH值并拷贝到数据包中
 if (esp->auth.icv_full_len) {
  err = esp_mac_digest(esp, skb, (u8 *)esph - skb->data,
         sizeof(*esph) + esp->conf.ivlen + clen);
  memcpy(pskb_put(skb, trailer, alen), esp->auth.work_icv, alen);
 }
// 重新计算外部IP头校验和
 ip_send_check(top_iph);
error:
 return err;
}
 
...... 待续 ......
分享到:
评论

相关推荐

    基于Rtd 2796方案的4K显示器方案:支持多种接口,适用于民用及专业应用领域,原理图与源代码共享

    内容概要:本文详细介绍了Rtd 2796这款高性能4K显示器控制器的技术特点和应用场景。Rtd 2796支持多种显示接口,包括LVDS、VBO和eDP,适用于民用、工业、矿山和医疗等多个领域。文中通过具体的代码示例和技术细节,解释了如何配置这些接口以及它们的应用优势。此外,文章还探讨了硬件设计的关键点,如电源时序控制、信号完整性处理和硬件级画面拼接等功能。针对不同应用场景的需求,Rtd 2796提供了稳定的显示解决方案,并附带了详细的原理图和源代码,帮助开发者加快开发进度。 适合人群:电子工程师、硬件设计师、嵌入式开发人员、显示技术爱好者。 使用场景及目标:①民用领域:4K显示器、智能电视和平板电脑;②工业领域:工业自动化设备和控制面板;③矿山领域:矿山监控系统和设备显示屏;④医疗领域:医疗设备显示屏和手术室监控系统。目标是为用户提供稳定、高性能的4K显示解决方案。 其他说明:文章提供了丰富的技术细节和代码示例,有助于深入理解和实际应用。对于希望深入了解4K显示器技术和Rtd 2796控制器的读者来说,是一份极具价值的参考资料。

    Rust音频处理实战:CPAL库实现实时音频流处理.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 Rust 以内存安全、零成本抽象和并发高效的特性,重塑编程体验。无需垃圾回收,却能通过所有权与借用检查机制杜绝空指针、数据竞争等隐患。从底层系统开发到 Web 服务构建,从物联网设备到高性能区块链,它凭借出色的性能和可靠性,成为开发者的全能利器。拥抱 Rust,解锁高效、安全编程新境界!

    航天电磁阀单元化制造工艺与质量管控.pdf

    航天电磁阀单元化制造工艺与质量管控.pdf

    Go语言编译器优化:-gcflags参数深度解析手册.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 编译闪电般迅速,并发性能卓越,部署轻松简单!Go 语言以极简设计理念和出色工程性能,成为云原生时代的首选编程语言。从 Docker 到 Kubernetes,全球顶尖科技企业都在采用 Go。点击了解 Go 语言的核心优势、实战窍门和未来走向,开启高效编程的全新体验!

    基于python实现进行股票分析和选股+源码+项目文档+使用说明(毕业设计&课程设计&项目开发)

    基于python实现进行股票分析和选股+源码+项目文档+使用说明,适合毕业设计、课程设计、项目开发。项目源码已经过严格测试,可以放心参考并在此基础上延申使用,详情见md文档 使用python进行股票历史数据下载和分析选股。除了选股策略以外,其他都可公开。 git网站上有很多优秀开源量化平台项目。本项目与其他项目的区别是,本项目侧重于选股、回测所需数据的导入工作。有了历史数据和选股策略,选择哪个量化平台做回测都是很轻松的事情了。 业余编程水平,需求导向。才疏学浅,刚学python几个月时间。git主要作为云端git库使用。无任何解答服务。 力求选择最稳定可靠的数据获取方式。虽然网上有很多数据源平台,但都受制于“积分”、带宽、平台是否更新等,完全是把程序主动权交到了对方手里。因此本项目所有数据依靠本地通达信软件导出提供

    Rust异步定时任务:TokioCron调度器.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 Rust 以内存安全、零成本抽象和并发高效的特性,重塑编程体验。无需垃圾回收,却能通过所有权与借用检查机制杜绝空指针、数据竞争等隐患。从底层系统开发到 Web 服务构建,从物联网设备到高性能区块链,它凭借出色的性能和可靠性,成为开发者的全能利器。拥抱 Rust,解锁高效、安全编程新境界!

    《基于多策略融合的改进麻雀搜索算法(SCSSA)复现研究》

    内容概要:本文详细介绍了融合正余弦和柯西变异的改进麻雀搜索算法(SCSSA)的复现过程。主要内容包括五个方面的改进策略:折射反向学习策略初始化、正余弦算法改进发现者策略、自适应调整系数、改进搜索因子以及柯西变异改进加入者策略。文中提供了具体的Python代码示例,展示了如何实现这些改进策略,并通过23个基准测试函数验证了SCSSA相比传统SSA的优越性能。此外,还通过图表分析了改进策略因子和搜索步长因子的变化,进一步证明了SCSSA的有效性。 适合人群:对优化算法感兴趣的科研人员、算法开发者以及相关领域的学生。 使用场景及目标:适用于需要解决复杂优化问题的研究和应用场合,旨在提高算法的全局搜索能力和跳出局部最优的能力,从而更快更精确地找到最优解。 其他说明:文章不仅提供了详细的理论解释和技术实现,还包括了丰富的实验数据和图表分析,帮助读者更好地理解和掌握SCSSA的工作原理和优势。

    verilog I2c设计代码

    rtl/axis_fifo.v rtl/i2c_init.v rtl/i2c_master_axil.v rtl/i2c_master_wbs_8.v rtl/i2c_master_wbs_16.v rtl/i2c_master.v rtl/i2c_single_reg.v rtl/i2c_slave_axil_master.v rtl/i2c_slave_wbm.v rtl/i2c_slave.v tb/test_i2c_init.v tb/test_i2c_master_axil.vtb/test_i2c_master_wbs_8.v tb/test_i2c_master_wbs_16.v tb/test_i2c_master.v tb/test_i2c_slave_axil_master.v tb/test_i2c_slave_wbm.v tb/test_i2c_slave.v

    适用于所有人的生成式AI-吴恩达

    适用于所有人的生成式AI-吴恩达:对于英文不佳的同学可看中文版课件

    binzi56_algorithm-pattern-c_30952_1746371906469.zip

    binzi56_algorithm-pattern-c_30952_1746371906469

    基于Stackelberg博弈的光伏用户群定价模型:优化电量共享,提升运营商与用户共赢策略

    内容概要:本文探讨了如何通过Stackelberg博弈模型解决光伏用户群之间的电量共享问题。当前分布式光伏上网政策限制了用户间的电量共享,导致资源利用效率低下。文中提出了一个多买方-多卖方的格局,运营商作为主导者制定内部电价,用户作为跟随者根据电价调整用电需求和光伏电量供应。通过MATLAB实现了模型的关键部分,包括参数定义、定价策略、用户需求响应以及寻找博弈均衡点。实验结果显示,该模型不仅提高了运营商的收益,还显著提升了用户的用电效益和光伏电量共享水平。 适合人群:对分布式能源系统、智能电网、博弈论及其应用感兴趣的科研人员和技术开发者。 使用场景及目标:适用于研究和开发分布式能源系统的优化调度方法,旨在提高光伏用户群的电量共享效率,促进能源的有效利用。 其他说明:文中详细介绍了模型的具体实现步骤和关键代码片段,提供了丰富的实验数据支持结论。此外,还讨论了实际应用中可能出现的问题及解决方案。

    ​​基于Swin Transformer与ASPP模块的图像分类系统设计与实现​

    基于Swin Transformer与ASPP模块的图像分类系统设计与实现 本文介绍了一种结合Swin Transformer与空洞空间金字塔池化(ASPP)模块的高效图像分类系统。该系统通过融合Transformer的全局建模能力和ASPP的多尺度特征提取优势,显著提升了模型在复杂场景下的分类性能。 模型架构创新 系统核心采用Swin Transformer作为骨干网络,其层次化窗口注意力机制能高效捕获长距离依赖关系。在特征提取阶段,创新性地引入ASPP模块,通过并行空洞卷积(膨胀率6/12/18)和全局平均池化分支,实现多尺度上下文信息融合。ASPP输出经1x1卷积降维后与原始特征拼接,有效增强了模型对物体尺寸变化的鲁棒性。 训练优化策略 训练流程采用Adam优化器(学习率0.0001)和交叉熵损失函数,支持多GPU并行训练。系统实现了完整的评估指标体系,包括准确率、精确率、召回率、特异度和F1分数等6项指标,并通过动态曲线可视化模块实时监控训练过程。采用早停机制保存最佳模型,验证集准确率提升可达3.2%。 工程实现亮点 1. 模块化设计:分离数据加载、模型构建和训练流程,支持快速迭代 2. 自动化评估:每轮训练自动生成指标报告和可视化曲线 3. 设备自适应:智能检测CUDA可用性,无缝切换训练设备 4. 中文支持:优化可视化界面的中文显示与负号渲染 实验表明,该系统在224×224分辨率图像分类任务中,仅需2个epoch即可达到92%以上的验证准确率。ASPP模块的引入使小目标识别准确率提升15%,特别适用于医疗影像等需要细粒度分类的场景。未来可通过轻量化改造进一步优化推理速度。

    【38字】MATLAB代码:综合能源系统碳交易与电制氢的热电优化研究【80字】

    内容概要:本文探讨了基于MATLAB和CPLEX仿真平台实现的考虑阶梯式碳交易机制与电制氢的综合能源系统热电优化。研究围绕碳交易、电制氢、阶梯式碳交易、综合能源系统热电优化等关键概念展开。通过引入阶梯式碳交易机制,使综合能源系统能够更好地控制碳排放。同时,细化电制氢过程,引入电解槽、甲烷反应器、氢燃料电池等设备,提高了氢能的利用效率。此外,提出了热电比可调的热电联产及氢燃料电池运行策略,提升了系统的灵活性和经济性。最终,通过设置购能成本、碳排放成本、弃风成本最小的目标函数并利用CPLEX求解,实现了对综合能源系统的优化。 适合人群:从事能源系统优化、碳交易机制研究、氢能技术开发的研究人员和技术人员。 使用场景及目标:适用于希望深入了解如何通过MATLAB和CPLEX实现综合能源系统优化的人士。目标是掌握如何在考虑阶梯式碳交易机制和电制氢的情况下,优化热电联产系统的运行,以达到节能减排的目的。 其他说明:文中详细介绍了各个模块的具体实现方法,包括碳交易机制的融入、电制氢过程的细化、热电联产与氢燃料电池运行策略的制定,以及目标函数的构建和求解策略。

    图书馆管理系统源代码.zip

    图书馆管理系统源代码.zip

    西门子S7-1200控制五轴伺服程序案例:结构化编程技巧与多模式应用,包括脉冲定位、速度模式与扭矩模式,博图版本兼容性分析

    内容概要:本文详细介绍了西门子S7-1200控制器在5轴伺服控制系统中的应用案例。主要内容涵盖PTO伺服轴脉冲定位控制、速度模式和扭矩模式的具体实现方法。通过具体的PLC指令如MC_Power、MC_MoveAbsolute、MC_MoveVelocity等展示了如何精确控制伺服电机的位置、速度和扭矩。此外,文章强调了结构化编程和功能模块化设计的重要性,包括自动/手动/单步模式切换、暂停后原位置继续运行、轴断电保持以及报警处理等功能模块的设计思路。同时,文中还提到了程序的兼容性和功能块的复用性,使得程序易于维护和扩展。 适合人群:从事自动化控制领域的工程师和技术人员,尤其是那些希望深入了解西门子S7-1200控制器及其在多轴伺服控制中应用的人群。 使用场景及目标:①帮助工程师理解和掌握西门子S7-1200控制器在复杂多轴伺服控制中的编程技巧;②提供实际工程案例供参考,便于快速应用于类似的工程项目;③提高系统的稳定性和可靠性,确保在工业生产环境中高效运作。 其他说明:本文提供了丰富的代码片段和详细的解释,有助于读者更好地理解和实践。同时,文中提到的结构化编程思想和模块化设计理念对于提升编程效率和代码质量非常有价值。

    Go语言JWT认证:自定义Claims与刷新令牌方案.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 编译闪电般迅速,并发性能卓越,部署轻松简单!Go 语言以极简设计理念和出色工程性能,成为云原生时代的首选编程语言。从 Docker 到 Kubernetes,全球顶尖科技企业都在采用 Go。点击了解 Go 语言的核心优势、实战窍门和未来走向,开启高效编程的全新体验!

    【自然语言处理】基于Transformer架构的NLP核心技术解析与应用进展:从语言理解到多模态拓展

    内容概要:本文详细介绍了自然语言处理(NLP)及其核心技术Transformer的发展与应用。首先阐述了NLP的核心任务,包括语言理解(词法、句法、语义分析)、语言生成(文本摘要、对话系统等)和应用扩展(故障报告分析、情感分析等)。接着重点解析了2017年提出的Transformer模型,它摒弃传统RNN/CNN,采用自注意力机制,具有编码器-解码器结构和多头注意力机制,显著提升了长距离依赖建模能力,并衍生出BERT、GPT、T5等预训练模型。此外,还介绍了NLP的技术进展,如基于大规模语料库的自监督学习和微调机制,以及多模态拓展。最后提及了YOLO与NLP在特定资源包中的间接联系,强调当前NLP技术以Transformer为核心,持续推动语言智能边界。; 适合人群:对自然语言处理及Transformer技术感兴趣的科研人员、开发者及相关领域的学生。; 使用场景及目标:①了解NLP的基本概念和发展历程;②深入理解Transformer架构及其在NLP中的应用;③掌握NLP的关键技术进展和未来发展方向。; 其他说明:本文虽然提到YOLO,但主要聚焦于NLP与Transformer,YOLO仅在特定情况下与NLP存在间接关联。

    西门子PLC 1214C压机控制程序:模块化编程实现压装逻辑编辑与多功能块集成控制,支持触摸屏操作

    内容概要:本文详细介绍了基于西门子PLC 1214C的压机控制系统,重点展示了模块化编程的应用及其优势。文中涵盖了多个功能块的具体实现,如压机控制、伺服控制、气缸控制、托盘坐标计算、基恩士扫码器集成等。每个功能块均采用SCL语言编写,具备良好的可移植性和灵活性。通过模块化设计,实现了复杂系统的高效管理和快速响应,显著提升了开发效率和调试便利性。 适合人群:从事工业自动化控制、PLC编程以及相关领域的工程师和技术人员。 使用场景及目标:适用于需要进行压机控制或其他类似工业自动化项目的场合。主要目标是提高编程效率、增强系统的可维护性和灵活性,同时降低开发成本和缩短项目周期。 其他说明:文章还提供了具体的代码示例,帮助读者更好地理解和应用模块化编程思想。此外,强调了模块化编程在实际项目中的重要性和优越性,鼓励读者尝试并掌握这一先进的编程方法。

    欧姆龙NB系列触摸屏配方程序'NB-Designer':含宏功能,200个配方组,已测试可直接套用

    内容概要:本文详细介绍了欧姆龙NB系列触摸屏配方程序的设计方法,主要利用索引寄存器和宏功能来实现高效的配方管理和搜索功能。文中首先阐述了项目背景,即在自动化项目中不同产品或工况需要不同的参数设置,因此配方功能至关重要。接着介绍了NB-Designer这一专用设计软件的功能特点及其在配方程序开发中的优势。然后深入探讨了索引寄存器的作用,将其比喻成地址簿,能够快速定位配方数据,并给出了具体的伪代码示例展示如何通过索引寄存器访问不同配方组的数据。此外,还讲解了宏功能的具体实现方式,如配方号搜索和配方名称搜索,提供了详细的代码片段。最后总结了这套配方程序的优点,强调其在实际项目中的稳定性和高效性。 适合人群:从事工业自动化领域的工程师和技术人员,尤其是那些需要处理复杂配方管理和搜索功能的人群。 使用场景及目标:适用于需要频繁更改参数设置的自动化生产线,如食品加工、制药等行业。目标是提高生产效率,减少人工干预,确保配方数据的准确性和实时性。 其他说明:本文不仅提供了理论指导,还附带了大量实际代码示例,便于读者理解和应用。同时,作者分享了许多实践经验,如优化搜索性能、处理设备重启后的配方恢复等,有助于读者在实际项目中少走弯路。

    Go语言正则表达式优化:DFA与NFA引擎对比分析.pdf

    文档支持目录章节跳转同时还支持阅读器左侧大纲显示和章节快速定位,文档内容完整、条理清晰。文档内所有文字、图表、函数、目录等元素均显示正常,无任何异常情况,敬请您放心查阅与使用。文档仅供学习参考,请勿用作商业用途。 编译闪电般迅速,并发性能卓越,部署轻松简单!Go 语言以极简设计理念和出色工程性能,成为云原生时代的首选编程语言。从 Docker 到 Kubernetes,全球顶尖科技企业都在采用 Go。点击了解 Go 语言的核心优势、实战窍门和未来走向,开启高效编程的全新体验!

Global site tag (gtag.js) - Google Analytics